From c4dd7a1a684490673e25aaf4fabec5df138854c4 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Thu, 14 Mar 2013 05:42:27 +0000 Subject: Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2. --- main/SAPI.c | 1094 +++++++++++++ main/SAPI.h | 317 ++++ main/alloca.c | 501 ++++++ main/build-defs.h.in | 91 ++ main/fopen_wrappers.c | 817 ++++++++++ main/fopen_wrappers.h | 64 + main/getopt.c | 199 +++ main/internal_functions.c.in | 48 + main/internal_functions_nw.c | 100 ++ main/internal_functions_win32.c | 208 +++ main/logos.h | 1080 +++++++++++++ main/main.c | 2645 +++++++++++++++++++++++++++++++ main/mergesort.c | 358 +++++ main/network.c | 1202 ++++++++++++++ main/output.c | 1578 ++++++++++++++++++ main/php.h | 457 ++++++ main/php_compat.h | 378 +++++ main/php_config.h.in | 2480 +++++++++++++++++++++++++++++ main/php_content_types.c | 100 ++ main/php_content_types.h | 31 + main/php_getopt.h | 57 + main/php_globals.h | 175 ++ main/php_ini.c | 916 +++++++++++ main/php_ini.h | 90 ++ main/php_logos.c | 99 ++ main/php_logos.h | 34 + main/php_main.h | 61 + main/php_memory_streams.h | 68 + main/php_network.h | 321 ++++ main/php_open_temporary_file.c | 314 ++++ main/php_open_temporary_file.h | 32 + main/php_output.h | 279 ++++ main/php_reentrancy.h | 133 ++ main/php_scandir.c | 136 ++ main/php_scandir.h | 54 + main/php_sprintf.c | 50 + main/php_streams.h | 611 +++++++ main/php_syslog.h | 52 + main/php_ticks.c | 86 + main/php_ticks.h | 41 + main/php_variables.c | 865 ++++++++++ main/php_variables.h | 49 + main/php_version.h | 8 + main/reentrancy.c | 450 ++++++ main/rfc1867.c | 1299 +++++++++++++++ main/rfc1867.h | 91 ++ main/snprintf.c | 1316 +++++++++++++++ main/snprintf.h | 179 +++ main/spprintf.c | 831 ++++++++++ main/spprintf.h | 52 + main/streams/cast.c | 418 +++++ main/streams/filter.c | 544 +++++++ main/streams/glob_wrapper.c | 291 ++++ main/streams/memory.c | 758 +++++++++ main/streams/mmap.c | 75 + main/streams/php_stream_context.h | 136 ++ main/streams/php_stream_filter_api.h | 162 ++ main/streams/php_stream_glob_wrapper.h | 44 + main/streams/php_stream_mmap.h | 88 + main/streams/php_stream_plain_wrapper.h | 66 + main/streams/php_stream_transport.h | 211 +++ main/streams/php_stream_userspace.h | 35 + main/streams/php_streams_int.h | 71 + main/streams/plain_wrapper.c | 1505 ++++++++++++++++++ main/streams/streams.c | 2397 ++++++++++++++++++++++++++++ main/streams/transports.c | 532 +++++++ main/streams/userspace.c | 1676 ++++++++++++++++++++ main/streams/xp_socket.c | 838 ++++++++++ main/strlcat.c | 106 ++ main/strlcpy.c | 103 ++ main/win32_internal_function_disabled.h | 34 + main/win95nt.h | 89 ++ 72 files changed, 32676 insertions(+) create mode 100644 main/SAPI.c create mode 100644 main/SAPI.h create mode 100644 main/alloca.c create mode 100644 main/build-defs.h.in create mode 100644 main/fopen_wrappers.c create mode 100644 main/fopen_wrappers.h create mode 100644 main/getopt.c create mode 100644 main/internal_functions.c.in create mode 100644 main/internal_functions_nw.c create mode 100644 main/internal_functions_win32.c create mode 100644 main/logos.h create mode 100644 main/main.c create mode 100644 main/mergesort.c create mode 100644 main/network.c create mode 100644 main/output.c create mode 100644 main/php.h create mode 100644 main/php_compat.h create mode 100644 main/php_config.h.in create mode 100644 main/php_content_types.c create mode 100644 main/php_content_types.h create mode 100644 main/php_getopt.h create mode 100644 main/php_globals.h create mode 100644 main/php_ini.c create mode 100644 main/php_ini.h create mode 100644 main/php_logos.c create mode 100644 main/php_logos.h create mode 100644 main/php_main.h create mode 100644 main/php_memory_streams.h create mode 100644 main/php_network.h create mode 100644 main/php_open_temporary_file.c create mode 100644 main/php_open_temporary_file.h create mode 100644 main/php_output.h create mode 100644 main/php_reentrancy.h create mode 100644 main/php_scandir.c create mode 100644 main/php_scandir.h create mode 100644 main/php_sprintf.c create mode 100644 main/php_streams.h create mode 100644 main/php_syslog.h create mode 100644 main/php_ticks.c create mode 100644 main/php_ticks.h create mode 100644 main/php_variables.c create mode 100644 main/php_variables.h create mode 100644 main/php_version.h create mode 100644 main/reentrancy.c create mode 100644 main/rfc1867.c create mode 100644 main/rfc1867.h create mode 100644 main/snprintf.c create mode 100644 main/snprintf.h create mode 100644 main/spprintf.c create mode 100644 main/spprintf.h create mode 100644 main/streams/cast.c create mode 100644 main/streams/filter.c create mode 100644 main/streams/glob_wrapper.c create mode 100644 main/streams/memory.c create mode 100644 main/streams/mmap.c create mode 100644 main/streams/php_stream_context.h create mode 100644 main/streams/php_stream_filter_api.h create mode 100644 main/streams/php_stream_glob_wrapper.h create mode 100644 main/streams/php_stream_mmap.h create mode 100644 main/streams/php_stream_plain_wrapper.h create mode 100644 main/streams/php_stream_transport.h create mode 100644 main/streams/php_stream_userspace.h create mode 100644 main/streams/php_streams_int.h create mode 100644 main/streams/plain_wrapper.c create mode 100644 main/streams/streams.c create mode 100644 main/streams/transports.c create mode 100644 main/streams/userspace.c create mode 100644 main/streams/xp_socket.c create mode 100644 main/strlcat.c create mode 100644 main/strlcpy.c create mode 100644 main/win32_internal_function_disabled.h create mode 100644 main/win95nt.h (limited to 'main') diff --git a/main/SAPI.c b/main/SAPI.c new file mode 100644 index 0000000..dcb2da6 --- /dev/null +++ b/main/SAPI.c @@ -0,0 +1,1094 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Original design: Shane Caraveo | + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include + +#include "php.h" +#include "SAPI.h" +#include "php_variables.h" +#include "php_ini.h" +#include "ext/standard/php_string.h" +#include "ext/standard/pageinfo.h" +#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) +#include "ext/pcre/php_pcre.h" +#endif +#ifdef ZTS +#include "TSRM.h" +#endif +#ifdef HAVE_SYS_TIME_H +#include +#elif defined(PHP_WIN32) +#include "win32/time.h" +#endif + +#include "rfc1867.h" + +#ifdef PHP_WIN32 +#define STRCASECMP stricmp +#else +#define STRCASECMP strcasecmp +#endif + +#include "php_content_types.h" + +#ifdef ZTS +SAPI_API int sapi_globals_id; +#else +sapi_globals_struct sapi_globals; +#endif + +static void sapi_globals_ctor(sapi_globals_struct *sapi_globals TSRMLS_DC) +{ + memset(sapi_globals, 0, sizeof(*sapi_globals)); + zend_hash_init_ex(&sapi_globals->known_post_content_types, 5, NULL, NULL, 1, 0); + php_setup_sapi_content_types(TSRMLS_C); +} + +static void sapi_globals_dtor(sapi_globals_struct *sapi_globals TSRMLS_DC) +{ + zend_hash_destroy(&sapi_globals->known_post_content_types); +} + +/* True globals (no need for thread safety) */ +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; + +#ifdef ZTS + ts_allocate_id(&sapi_globals_id, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor); +# ifdef PHP_WIN32 + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); +# endif +#else + sapi_globals_ctor(&sapi_globals); +#endif + + virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */ + +#ifdef PHP_WIN32 + tsrm_win32_startup(); +#endif + + reentrancy_startup(); +} + +SAPI_API void sapi_shutdown(void) +{ +#ifdef ZTS + ts_free_id(sapi_globals_id); +#else + sapi_globals_dtor(&sapi_globals); +#endif + + reentrancy_shutdown(); + + virtual_cwd_shutdown(); + +#ifdef PHP_WIN32 + tsrm_win32_shutdown(); +#endif +} + + +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) +{ + if (SG(request_info).post_entry && SG(request_info).content_type_dup) { + SG(request_info).post_entry->post_handler(SG(request_info).content_type_dup, arg TSRMLS_CC); + if (SG(request_info).post_data) { + efree(SG(request_info).post_data); + SG(request_info).post_data = NULL; + } + efree(SG(request_info).content_type_dup); + SG(request_info).content_type_dup = NULL; + } +} + +static void sapi_read_post_data(TSRMLS_D) +{ + sapi_post_entry *post_entry; + uint content_type_length = strlen(SG(request_info).content_type); + char *content_type = estrndup(SG(request_info).content_type, content_type_length); + char *p; + char oldchar=0; + void (*post_reader_func)(TSRMLS_D) = NULL; + + + /* dedicated implementation for increased performance: + * - Make the content type lowercase + * - Trim descriptive data, stay with the content-type only + */ + for (p=content_type; ppost_reader; + } else { + /* fallback */ + SG(request_info).post_entry = NULL; + if (!sapi_module.default_post_reader) { + /* no default reader ? */ + SG(request_info).content_type_dup = NULL; + sapi_module.sapi_error(E_WARNING, "Unsupported content type: '%s'", content_type); + return; + } + } + if (oldchar) { + *(p-1) = oldchar; + } + + SG(request_info).content_type_dup = content_type; + + if(post_reader_func) { + post_reader_func(TSRMLS_C); + } + + if(sapi_module.default_post_reader) { + sapi_module.default_post_reader(TSRMLS_C); + } +} + + +SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data) +{ + int read_bytes; + int allocated_bytes=SAPI_POST_BLOCK_SIZE+1; + + if ((SG(post_max_size) > 0) && (SG(request_info).content_length > SG(post_max_size))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "POST Content-Length of %ld bytes exceeds the limit of %ld bytes", + SG(request_info).content_length, SG(post_max_size)); + return; + } + SG(request_info).post_data = emalloc(allocated_bytes); + + for (;;) { + read_bytes = sapi_module.read_post(SG(request_info).post_data+SG(read_post_bytes), SAPI_POST_BLOCK_SIZE TSRMLS_CC); + if (read_bytes<=0) { + break; + } + SG(read_post_bytes) += read_bytes; + if ((SG(post_max_size) > 0) && (SG(read_post_bytes) > SG(post_max_size))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Actual POST length does not match Content-Length, and exceeds %ld bytes", SG(post_max_size)); + break; + } + if (read_bytes < SAPI_POST_BLOCK_SIZE) { + break; + } + if (SG(read_post_bytes)+SAPI_POST_BLOCK_SIZE >= allocated_bytes) { + allocated_bytes = SG(read_post_bytes)+SAPI_POST_BLOCK_SIZE+1; + SG(request_info).post_data = erealloc(SG(request_info).post_data, allocated_bytes); + } + } + SG(request_info).post_data[SG(read_post_bytes)] = 0; /* terminating NULL */ + SG(request_info).post_data_length = SG(read_post_bytes); +} + + +static inline char *get_default_content_type(uint prefix_len, uint *len TSRMLS_DC) +{ + char *mimetype, *charset, *content_type; + uint mimetype_len, charset_len; + + 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 (*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 { + *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) +{ + 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); +} + +/* + * Add charset on content-type header if the MIME type starts with + * "text/", the default_charset directive is not empty and + * there is not already a charset option in there. + * + * If "mimetype" is non-NULL, it should point to a pointer allocated + * with emalloc(). If a charset is added, the string will be + * re-allocated and the new length is returned. If mimetype is + * unchanged, 0 is returned. + * + */ +SAPI_API size_t sapi_apply_default_charset(char **mimetype, size_t len TSRMLS_DC) +{ + char *charset, *newtype; + size_t newlen; + charset = SG(default_charset) ? SG(default_charset) : SAPI_DEFAULT_CHARSET; + + if (*mimetype != NULL) { + if (*charset && strncmp(*mimetype, "text/", 5) == 0 && strstr(*mimetype, "charset=") == NULL) { + newlen = len + (sizeof(";charset=")-1) + strlen(charset); + newtype = emalloc(newlen + 1); + PHP_STRLCPY(newtype, *mimetype, newlen + 1, len); + strlcat(newtype, ";charset=", newlen + 1); + strlcat(newtype, charset, newlen + 1); + efree(*mimetype); + *mimetype = newtype; + return newlen; + } + } + return 0; +} + +SAPI_API void sapi_activate_headers_only(TSRMLS_D) +{ + if (SG(request_info).headers_read == 1) + return; + SG(request_info).headers_read = 1; + zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), + (void (*)(void *)) sapi_free_header, 0); + SG(sapi_headers).send_default_content_type = 1; + + /* SG(sapi_headers).http_response_code = 200; */ + SG(sapi_headers).http_status_line = NULL; + SG(sapi_headers).mimetype = NULL; + SG(read_post_bytes) = 0; + SG(request_info).post_data = NULL; + SG(request_info).raw_post_data = NULL; + SG(request_info).current_user = NULL; + SG(request_info).current_user_length = 0; + SG(request_info).no_headers = 0; + SG(request_info).post_entry = NULL; + SG(global_request_time) = 0; + + /* + * 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 { + SG(request_info).headers_only = 0; + } + if (SG(server_context)) { + 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 ) { + sapi_module.input_filter_init(TSRMLS_C); + } +} + +/* + * Called from php_request_startup() for every request. + */ + +SAPI_API void sapi_activate(TSRMLS_D) +{ + zend_llist_init(&SG(sapi_headers).headers, sizeof(sapi_header_struct), (void (*)(void *)) sapi_free_header, 0); + SG(sapi_headers).send_default_content_type = 1; + + /* + SG(sapi_headers).http_response_code = 200; + */ + 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; + SG(request_info).current_user = NULL; + SG(request_info).current_user_length = 0; + SG(request_info).no_headers = 0; + SG(request_info).post_entry = NULL; + 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. */ + if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) { + SG(request_info).headers_only = 1; + } else { + SG(request_info).headers_only = 0; + } + SG(rfc1867_uploaded_files) = NULL; + + /* Handle request method */ + if (SG(server_context)) { + 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 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) { + sapi_module.default_post_reader(TSRMLS_C); + } + } + } else { + SG(request_info).content_type_dup = NULL; + } + + /* 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) { + sapi_module.input_filter_init(TSRMLS_C); + } +} + + +static void sapi_send_headers_free(TSRMLS_D) +{ + if (SG(sapi_headers).http_status_line) { + efree(SG(sapi_headers).http_status_line); + SG(sapi_headers).http_status_line = NULL; + } +} + +SAPI_API void sapi_deactivate(TSRMLS_D) +{ + zend_llist_destroy(&SG(sapi_headers).headers); + if (SG(request_info).post_data) { + efree(SG(request_info).post_data); + } else if (SG(server_context)) { + if(sapi_module.read_post) { + /* make sure we've consumed all request input data */ + char dummy[SAPI_POST_BLOCK_SIZE]; + int read_bytes; + + while((read_bytes = sapi_module.read_post(dummy, sizeof(dummy)-1 TSRMLS_CC)) > 0) { + SG(read_post_bytes) += read_bytes; + } + } + } + if (SG(request_info).raw_post_data) { + efree(SG(request_info).raw_post_data); + } + if (SG(request_info).auth_user) { + efree(SG(request_info).auth_user); + } + if (SG(request_info).auth_password) { + efree(SG(request_info).auth_password); + } + if (SG(request_info).auth_digest) { + efree(SG(request_info).auth_digest); + } + if (SG(request_info).content_type_dup) { + efree(SG(request_info).content_type_dup); + } + if (SG(request_info).current_user) { + efree(SG(request_info).current_user); + } + if (sapi_module.deactivate) { + sapi_module.deactivate(TSRMLS_C); + } + if (SG(rfc1867_uploaded_files)) { + destroy_uploaded_files_hash(TSRMLS_C); + } + if (SG(sapi_headers).mimetype) { + efree(SG(sapi_headers).mimetype); + SG(sapi_headers).mimetype = NULL; + } + 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; +} + + +SAPI_API void sapi_initialize_empty_request(TSRMLS_D) +{ + SG(server_context) = NULL; + SG(request_info).request_method = NULL; + SG(request_info).auth_digest = SG(request_info).auth_user = SG(request_info).auth_password = NULL; + SG(request_info).content_type_dup = NULL; +} + + +static int sapi_extract_response_code(const char *header_line) +{ + int code = 200; + const char *ptr; + + for (ptr = header_line; *ptr; ptr++) { + if (*ptr == ' ' && *(ptr + 1) != ' ') { + code = atoi(ptr + 1); + break; + } + } + + return code; +} + + +static void sapi_update_response_code(int ncode TSRMLS_DC) +{ + /* if the status code did not change, we do not want + to change the status line, and no need to change the code */ + if (SG(sapi_headers).http_response_code == ncode) { + return; + } + + if (SG(sapi_headers).http_status_line) { + efree(SG(sapi_headers).http_status_line); + SG(sapi_headers).http_status_line = NULL; + } + SG(sapi_headers).http_response_code = ncode; +} + +/* + * since zend_llist_del_element only remove one matched item once, + * we should remove them by ourself + */ +static void sapi_remove_header(zend_llist *l, char *name, uint len) { + sapi_header_struct *header; + zend_llist_element *next; + zend_llist_element *current=l->head; + + while (current) { + header = (sapi_header_struct *)(current->data); + next = current->next; + if (header->header_len > len && header->header[len] == ':' + && !strncasecmp(header->header, name, len)) { + if (current->prev) { + current->prev->next = next; + } else { + l->head = next; + } + if (next) { + next->prev = current->prev; + } else { + l->tail = current->prev; + } + sapi_free_header(header); + efree(current); + --l->count; + } + current = next; + } +} + +SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC) +{ + sapi_header_line ctr = {0}; + int r; + + ctr.line = header_line; + ctr.line_len = header_line_len; + + r = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, + &ctr TSRMLS_CC); + + if (!duplicate) + efree(header_line); + + 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) +{ + sapi_header_struct sapi_header; + char *colon_offset; + char *header_line; + uint header_line_len; + int http_response_code; + + if (SG(headers_sent) && !SG(request_info).no_headers) { + 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)", + output_start_filename, output_start_lineno); + } else { + sapi_module.sapi_error(E_WARNING, "Cannot modify header information - headers already sent"); + } + return FAILURE; + } + + switch (op) { + case SAPI_HEADER_SET_STATUS: + sapi_update_response_code((int)(zend_intptr_t) arg TSRMLS_CC); + return SUCCESS; + + case SAPI_HEADER_ADD: + case SAPI_HEADER_REPLACE: + case SAPI_HEADER_DELETE: { + sapi_header_line *p = arg; + + if (!p->line || !p->line_len) { + return FAILURE; + } + header_line = p->line; + header_line_len = p->line_len; + http_response_code = p->response_code; + break; + } + + case SAPI_HEADER_DELETE_ALL: + if (sapi_module.header_handler) { + sapi_module.header_handler(&sapi_header, op, &SG(sapi_headers) TSRMLS_CC); + } + zend_llist_clean(&SG(sapi_headers).headers); + return SUCCESS; + + default: + return FAILURE; + } + + header_line = estrndup(header_line, header_line_len); + + /* cut of trailing spaces, linefeeds and carriage-returns */ + 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, ':')) { + efree(header_line); + 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; + for (i = 0; i < header_line_len; i++) { + /* RFC 2616 allows new lines if followed by SP or HT */ + int illegal_break = + (header_line[i+1] != ' ' && header_line[i+1] != '\t') + && ( + header_line[i] == '\n' + || (header_line[i] == '\r' && header_line[i+1] != '\n')); + if (illegal_break) { + efree(header_line); + sapi_module.sapi_error(E_WARNING, "Header may not contain " + "more than a single header, new line detected"); + return FAILURE; + } + if (header_line[i] == '\0') { + efree(header_line); + sapi_module.sapi_error(E_WARNING, "Header may not contain NUL bytes"); + return FAILURE; + } + } + } + + sapi_header.header = header_line; + sapi_header.header_len = header_line_len; + + /* 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)) { + /* filter out the response code */ + sapi_update_response_code(sapi_extract_response_code(header_line) TSRMLS_CC); + /* sapi_update_response_code doesn't free the status line if the code didn't change */ + if (SG(sapi_headers).http_status_line) { + efree(SG(sapi_headers).http_status_line); + } + SG(sapi_headers).http_status_line = header_line; + return SUCCESS; + } else { + colon_offset = strchr(header_line, ':'); + if (colon_offset) { + *colon_offset = 0; + if (!STRCASECMP(header_line, "Content-Type")) { + char *ptr = colon_offset+1, *mimetype = NULL, *newheader; + size_t len = header_line_len - (ptr - header_line), newlen; + while (*ptr == ' ') { + ptr++; + len--; + } + + /* Disable possible output compression for images */ + if (!strncmp(ptr, "image/", sizeof("image/")-1)) { + zend_alter_ini_entry("zlib.output_compression", sizeof("zlib.output_compression"), "0", sizeof("0") - 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + } + + mimetype = estrdup(ptr); + newlen = sapi_apply_default_charset(&mimetype, len TSRMLS_CC); + if (!SG(sapi_headers).mimetype){ + SG(sapi_headers).mimetype = estrdup(mimetype); + } + + if (newlen != 0) { + newlen += sizeof("Content-type: "); + newheader = emalloc(newlen); + PHP_STRLCPY(newheader, "Content-type: ", newlen, sizeof("Content-type: ")-1); + strlcat(newheader, mimetype, newlen); + sapi_header.header = newheader; + sapi_header.header_len = newlen - 1; + efree(header_line); + } + 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) && + SG(sapi_headers).http_response_code != 201) { + /* Return a Found Redirect if one is not already specified */ + if (http_response_code) { /* user specified redirect code */ + sapi_update_response_code(http_response_code TSRMLS_CC); + } else if (SG(request_info).proto_num > 1000 && + SG(request_info).request_method && + strcmp(SG(request_info).request_method, "HEAD") && + strcmp(SG(request_info).request_method, "GET")) { + sapi_update_response_code(303 TSRMLS_CC); + } else { + sapi_update_response_code(302 TSRMLS_CC); + } + } + } else if (!STRCASECMP(header_line, "WWW-Authenticate")) { /* HTTP Authentication */ + sapi_update_response_code(401 TSRMLS_CC); /* authentication-required */ + } + if (sapi_header.header==header_line) { + *colon_offset = ':'; + } + } + } + if (http_response_code) { + sapi_update_response_code(http_response_code TSRMLS_CC); + } + sapi_header_add_op(op, &sapi_header TSRMLS_CC); + return SUCCESS; +} + + +SAPI_API int sapi_send_headers(TSRMLS_D) +{ + int retval; + int ret = FAILURE; + + if (SG(headers_sent) || SG(request_info).no_headers || SG(callback_run)) { + return SUCCESS; + } + + /* Success-oriented. We set headers_sent to 1 here to avoid an infinite loop + * in case of an error situation. + */ + if (SG(sapi_headers).send_default_content_type && sapi_module.send_headers) { + sapi_header_struct default_header; + 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; + + if (sapi_module.send_headers) { + retval = sapi_module.send_headers(&SG(sapi_headers) TSRMLS_CC); + } else { + retval = SAPI_HEADER_DO_SEND; + } + + switch (retval) { + case SAPI_HEADER_SENT_SUCCESSFULLY: + ret = SUCCESS; + break; + case SAPI_HEADER_DO_SEND: { + sapi_header_struct http_status_line; + char buf[255]; + + if (SG(sapi_headers).http_status_line) { + http_status_line.header = SG(sapi_headers).http_status_line; + http_status_line.header_len = strlen(SG(sapi_headers).http_status_line); + } else { + http_status_line.header = buf; + http_status_line.header_len = slprintf(buf, sizeof(buf), "HTTP/1.0 %d X", SG(sapi_headers).http_response_code); + } + sapi_module.send_header(&http_status_line, SG(server_context) TSRMLS_CC); + } + zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) sapi_module.send_header, SG(server_context) TSRMLS_CC); + if(SG(sapi_headers).send_default_content_type) { + sapi_header_struct default_header; + + sapi_get_default_content_type_header(&default_header TSRMLS_CC); + sapi_module.send_header(&default_header, SG(server_context) TSRMLS_CC); + sapi_free_header(&default_header); + } + sapi_module.send_header(NULL, SG(server_context) TSRMLS_CC); + ret = SUCCESS; + break; + case SAPI_HEADER_SEND_FAILED: + SG(headers_sent) = 0; + ret = FAILURE; + break; + } + + sapi_send_headers_free(TSRMLS_C); + + return ret; +} + + +SAPI_API int sapi_register_post_entries(sapi_post_entry *post_entries TSRMLS_DC) +{ + sapi_post_entry *p=post_entries; + + while (p->content_type) { + if (sapi_register_post_entry(p TSRMLS_CC) == FAILURE) { + return FAILURE; + } + p++; + } + return SUCCESS; +} + + +SAPI_API int sapi_register_post_entry(sapi_post_entry *post_entry TSRMLS_DC) +{ + if (SG(sapi_started) && EG(in_execution)) { + return FAILURE; + } + return zend_hash_add(&SG(known_post_content_types), + post_entry->content_type, post_entry->content_type_len+1, + (void *) post_entry, sizeof(sapi_post_entry), NULL); +} + +SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry TSRMLS_DC) +{ + if (SG(sapi_started) && EG(in_execution)) { + return; + } + zend_hash_del(&SG(known_post_content_types), post_entry->content_type, + post_entry->content_type_len+1); +} + + +SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D) TSRMLS_DC) +{ + if (SG(sapi_started) && EG(in_execution)) { + return FAILURE; + } + sapi_module.default_post_reader = default_post_reader; + return SUCCESS; +} + + +SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC) TSRMLS_DC) +{ + if (SG(sapi_started) && EG(in_execution)) { + return FAILURE; + } + sapi_module.treat_data = treat_data; + 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) TSRMLS_DC) +{ + if (SG(sapi_started) && EG(in_execution)) { + return FAILURE; + } + sapi_module.input_filter = input_filter; + sapi_module.input_filter_init = input_filter_init; + return SUCCESS; +} + +SAPI_API int sapi_flush(TSRMLS_D) +{ + if (sapi_module.flush) { + sapi_module.flush(SG(server_context)); + return SUCCESS; + } else { + return FAILURE; + } +} + +SAPI_API struct stat *sapi_get_stat(TSRMLS_D) +{ + if (sapi_module.get_stat) { + return sapi_module.get_stat(TSRMLS_C); + } else { + if (!SG(request_info).path_translated || (VCWD_STAT(SG(request_info).path_translated, &SG(global_stat)) == -1)) { + return NULL; + } + return &SG(global_stat); + } +} + +SAPI_API char *sapi_getenv(char *name, size_t name_len TSRMLS_DC) +{ + if (sapi_module.getenv) { + char *value, *tmp = sapi_module.getenv(name, name_len TSRMLS_CC); + if (tmp) { + value = estrdup(tmp); + } else { + return NULL; + } + if (sapi_module.input_filter) { + sapi_module.input_filter(PARSE_STRING, name, &value, strlen(value), NULL TSRMLS_CC); + } + return value; + } + return NULL; +} + +SAPI_API int sapi_get_fd(int *fd TSRMLS_DC) +{ + if (sapi_module.get_fd) { + return sapi_module.get_fd(fd TSRMLS_CC); + } else { + return FAILURE; + } +} + +SAPI_API int sapi_force_http_10(TSRMLS_D) +{ + if (sapi_module.force_http_10) { + return sapi_module.force_http_10(TSRMLS_C); + } else { + return FAILURE; + } +} + + +SAPI_API int sapi_get_target_uid(uid_t *obj TSRMLS_DC) +{ + if (sapi_module.get_target_uid) { + return sapi_module.get_target_uid(obj TSRMLS_CC); + } else { + return FAILURE; + } +} + +SAPI_API int sapi_get_target_gid(gid_t *obj TSRMLS_DC) +{ + if (sapi_module.get_target_gid) { + return sapi_module.get_target_gid(obj TSRMLS_CC); + } else { + return FAILURE; + } +} + +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 { + 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); +} + +SAPI_API void sapi_terminate_process(TSRMLS_D) { + if (sapi_module.terminate_process) { + sapi_module.terminate_process(TSRMLS_C); + } +} + +/* + * 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/SAPI.h b/main/SAPI.h new file mode 100644 index 0000000..92b7329 --- /dev/null +++ b/main/SAPI.h @@ -0,0 +1,317 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef SAPI_H +#define SAPI_H + +#include "zend.h" +#include "zend_API.h" +#include "zend_llist.h" +#include "zend_operators.h" +#ifdef PHP_WIN32 +#include "win95nt.h" +#endif +#include + +#define SAPI_OPTION_NO_CHDIR 1 + +#define SAPI_POST_BLOCK_SIZE 4000 + +#ifdef PHP_WIN32 +# ifdef SAPI_EXPORTS +# define SAPI_API __declspec(dllexport) +# else +# define SAPI_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define SAPI_API __attribute__ ((visibility("default"))) +#else +# define SAPI_API +#endif + +#undef shutdown + +typedef struct { + char *header; + uint header_len; +} sapi_header_struct; + + +typedef struct { + zend_llist headers; + int http_response_code; + unsigned char send_default_content_type; + char *mimetype; + char *http_status_line; +} sapi_headers_struct; + + +typedef struct _sapi_post_entry sapi_post_entry; +typedef struct _sapi_module_struct sapi_module_struct; + +BEGIN_EXTERN_C() +extern SAPI_API sapi_module_struct sapi_module; /* true global */ +END_EXTERN_C() + +/* Some values in this structure needs to be filled in before + * calling sapi_activate(). We WILL change the `char *' entries, + * so make sure that you allocate a separate buffer for them + * and that you free them after sapi_deactivate(). + */ + +typedef struct { + const char *request_method; + char *query_string; + char *post_data, *raw_post_data; + char *cookie_data; + long content_length; + uint post_data_length, raw_post_data_length; + + char *path_translated; + char *request_uri; + + const char *content_type; + + zend_bool headers_only; + zend_bool no_headers; + zend_bool headers_read; + + sapi_post_entry *post_entry; + + char *content_type_dup; + + /* for HTTP authentication */ + char *auth_user; + char *auth_password; + char *auth_digest; + + /* this is necessary for the CGI SAPI module */ + char *argv0; + + char *current_user; + int current_user_length; + + /* this is necessary for CLI module */ + int argc; + char **argv; + int proto_num; +} sapi_request_info; + + +typedef struct _sapi_globals_struct { + void *server_context; + sapi_request_info request_info; + sapi_headers_struct sapi_headers; + int read_post_bytes; + unsigned char headers_sent; + struct stat global_stat; + char *default_mimetype; + char *default_charset; + HashTable *rfc1867_uploaded_files; + long post_max_size; + int options; + zend_bool sapi_started; + 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; + + +BEGIN_EXTERN_C() +#ifdef ZTS +# define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v) +SAPI_API extern int sapi_globals_id; +#else +# define SG(v) (sapi_globals.v) +extern SAPI_API sapi_globals_struct sapi_globals; +#endif + +SAPI_API void sapi_startup(sapi_module_struct *sf); +SAPI_API void sapi_shutdown(void); +SAPI_API void sapi_activate(TSRMLS_D); +SAPI_API void sapi_deactivate(TSRMLS_D); +SAPI_API void sapi_initialize_empty_request(TSRMLS_D); +END_EXTERN_C() + +/* + * This is the preferred and maintained API for + * operating on HTTP headers. + */ + +/* + * Always specify a sapi_header_line this way: + * + * sapi_header_line ctr = {0}; + */ + +typedef struct { + char *line; /* If you allocated this, you need to free it yourself */ + uint line_len; + long response_code; /* long due to zend_parse_parameters compatibility */ +} sapi_header_line; + +typedef enum { /* Parameter: */ + SAPI_HEADER_REPLACE, /* sapi_header_line* */ + SAPI_HEADER_ADD, /* sapi_header_line* */ + SAPI_HEADER_DELETE, /* sapi_header_line* */ + SAPI_HEADER_DELETE_ALL, /* void */ + SAPI_HEADER_SET_STATUS /* int */ +} sapi_header_op_enum; + +BEGIN_EXTERN_C() +SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC); + +/* Deprecated functions. Use sapi_header_op instead. */ +SAPI_API int sapi_add_header_ex(char *header_line, uint header_line_len, zend_bool duplicate, zend_bool replace TSRMLS_DC); +#define sapi_add_header(a, b, c) sapi_add_header_ex((a),(b),(c),1 TSRMLS_CC) + + +SAPI_API int sapi_send_headers(TSRMLS_D); +SAPI_API void sapi_free_header(sapi_header_struct *sapi_header); +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) 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); +SAPI_API char *sapi_getenv(char *name, size_t name_len TSRMLS_DC); + +SAPI_API char *sapi_get_default_content_type(TSRMLS_D); +SAPI_API void sapi_get_default_content_type_header(sapi_header_struct *default_header TSRMLS_DC); +SAPI_API size_t sapi_apply_default_charset(char **mimetype, size_t len TSRMLS_DC); +SAPI_API void sapi_activate_headers_only(TSRMLS_D); + +SAPI_API int sapi_get_fd(int *fd TSRMLS_DC); +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 double sapi_get_request_time(TSRMLS_D); +SAPI_API void sapi_terminate_process(TSRMLS_D); +END_EXTERN_C() + +struct _sapi_module_struct { + char *name; + char *pretty_name; + + int (*startup)(struct _sapi_module_struct *sapi_module); + int (*shutdown)(struct _sapi_module_struct *sapi_module); + + int (*activate)(TSRMLS_D); + int (*deactivate)(TSRMLS_D); + + int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC); + void (*flush)(void *server_context); + struct stat *(*get_stat)(TSRMLS_D); + char *(*getenv)(char *name, size_t name_len TSRMLS_DC); + + void (*sapi_error)(int type, const char *error_msg, ...); + + int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC); + int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC); + void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC); + + int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC); + char *(*read_cookies)(TSRMLS_D); + + void (*register_server_variables)(zval *track_vars_array TSRMLS_DC); + void (*log_message)(char *message TSRMLS_DC); + double (*get_request_time)(TSRMLS_D); + void (*terminate_process)(TSRMLS_D); + + char *php_ini_path_override; + + void (*block_interruptions)(void); + void (*unblock_interruptions)(void); + + void (*default_post_reader)(TSRMLS_D); + void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC); + 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); + + int (*force_http_10)(TSRMLS_D); + + int (*get_target_uid)(uid_t * TSRMLS_DC); + int (*get_target_gid)(gid_t * TSRMLS_DC); + + unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC); + + void (*ini_defaults)(HashTable *configuration_hash); + int phpinfo_as_text; + + char *ini_entries; + const zend_function_entry *additional_functions; + unsigned int (*input_filter_init)(TSRMLS_D); +}; + + +struct _sapi_post_entry { + char *content_type; + uint content_type_len; + void (*post_reader)(TSRMLS_D); + void (*post_handler)(char *content_type_dup, void *arg TSRMLS_DC); +}; + +/* header_handler() constants */ +#define SAPI_HEADER_ADD (1<<0) + + +#define SAPI_HEADER_SENT_SUCCESSFULLY 1 +#define SAPI_HEADER_DO_SEND 2 +#define SAPI_HEADER_SEND_FAILED 3 + +#define SAPI_DEFAULT_MIMETYPE "text/html" +#define SAPI_DEFAULT_CHARSET "" +#define SAPI_PHP_VERSION_HEADER "X-Powered-By: PHP/" PHP_VERSION + +#define SAPI_POST_READER_FUNC(post_reader) void post_reader(TSRMLS_D) +#define SAPI_POST_HANDLER_FUNC(post_handler) void post_handler(char *content_type_dup, void *arg TSRMLS_DC) + +#define SAPI_TREAT_DATA_FUNC(treat_data) void treat_data(int arg, char *str, zval* destArray TSRMLS_DC) +#define SAPI_INPUT_FILTER_FUNC(input_filter) unsigned int input_filter(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC) + +BEGIN_EXTERN_C() +SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data); +SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); +SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data); +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter); +END_EXTERN_C() + +#define STANDARD_SAPI_MODULE_PROPERTIES + +#endif /* SAPI_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/alloca.c b/main/alloca.c new file mode 100644 index 0000000..401649c --- /dev/null +++ b/main/alloca.c @@ -0,0 +1,501 @@ +/* alloca.c -- allocate automatically reclaimed memory + (Mostly) portable public-domain implementation -- D A Gwyn + + This implementation of the PWB library alloca function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + J.Otto Tennant contributed the Cray support. + + There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. */ + +/* $Id$ */ + +#include + +#if !HAVE_ALLOCA + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef emacs +#include "blockinput.h" +#endif + +/* If compiling with GCC 2, this file's not needed. */ +#if !defined (__GNUC__) || __GNUC__ < 2 + +/* If someone has defined alloca as a macro, + there must be some other way alloca is supposed to work. */ +#ifndef alloca + +#ifdef emacs +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif /* static */ +#endif /* emacs */ + +/* If your stack is a linked list of frames, you have to + provide an "address metric" ADDRESS_FUNCTION macro. */ + +#if defined (CRAY) && defined (CRAY_STACKSEG_END) +long i00afunc (); +#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg)) +#else +#define ADDRESS_FUNCTION(arg) &(arg) +#endif + +#if __STDC__ +typedef void *pointer; +#else +typedef char *pointer; +#endif + +#ifndef NULL +#define NULL 0 +#endif + +/* Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* Direction unknown. */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */ + +#else /* STACK_DIRECTION == 0; need run-time code. */ + +static int stack_dir; /* 1 or -1 once known. */ +#define STACK_DIR stack_dir + +static void +find_stack_direction () +{ + static char *addr = NULL; /* Address of first `dummy', once known. */ + auto char dummy; /* To get stack address. */ + + if (addr == NULL) + { /* Initial entry. */ + addr = ADDRESS_FUNCTION (dummy); + + find_stack_direction (); /* Recurse once. */ + } + else + { + /* Second entry. */ + if (ADDRESS_FUNCTION (dummy) > addr) + stack_dir = 1; /* Stack grew upward. */ + else + stack_dir = -1; /* Stack grew downward. */ + } +} + +#endif /* STACK_DIRECTION == 0 */ + +/* An "alloca header" is used to: + (a) chain together all alloca'ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc + alignment chunk size. The following default should work okay. */ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* To force sizeof(header). */ + struct + { + union hdr *next; /* For chaining headers. */ + char *deep; /* For stack depth measure. */ + } h; +} header; + +static header *last_alloca_header = NULL; /* -> last alloca header. */ + +/* Return a pointer to at least SIZE bytes of storage, + which will be automatically reclaimed upon exit from + the procedure that called alloca. Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. */ + +pointer +alloca (size) + size_t size; +{ + auto char probe; /* Probes stack depth: */ + register char *depth = ADDRESS_FUNCTION (probe); + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* Unknown growth direction. */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca'd storage that + was allocated from deeper in the stack than currently. */ + + { + register header *hp; /* Traverses linked list. */ + +#ifdef emacs + BLOCK_INPUT; +#endif + + for (hp = last_alloca_header; hp != NULL;) + if ((STACK_DIR > 0 && hp->h.deep > depth) + || (STACK_DIR < 0 && hp->h.deep < depth)) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* Collect garbage. */ + + hp = np; /* -> next header. */ + } + else + break; /* Rest are not deeper. */ + + last_alloca_header = hp; /* -> last valid storage. */ + +#ifdef emacs + UNBLOCK_INPUT; +#endif + } + + if (size == 0) + return NULL; /* No allocation required. */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = malloc (sizeof (header) + size); + /* Address of header. */ + + if (new == 0) + abort(); + + ((header *) new)->h.next = last_alloca_header; + ((header *) new)->h.deep = depth; + + last_alloca_header = (header *) new; + + /* User storage begins just after header. */ + + return (pointer) ((char *) new + sizeof (header)); + } +} + +#if defined (CRAY) && defined (CRAY_STACKSEG_END) + +#ifdef DEBUG_I00AFUNC +#include +#endif + +#ifndef CRAY_STACK +#define CRAY_STACK +#ifndef CRAY2 +/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */ +struct stack_control_header + { + long shgrow:32; /* Number of times stack has grown. */ + long shaseg:32; /* Size of increments to stack. */ + long shhwm:32; /* High water mark of stack. */ + long shsize:32; /* Current size of stack (all segments). */ + }; + +/* The stack segment linkage control information occurs at + the high-address end of a stack segment. (The stack + grows from low addresses to high addresses.) The initial + part of the stack segment linkage control information is + 0200 (octal) words. This provides for register storage + for the routine which overflows the stack. */ + +struct stack_segment_linkage + { + long ss[0200]; /* 0200 overflow words. */ + long sssize:32; /* Number of words in this segment. */ + long ssbase:32; /* Offset to stack base. */ + long:32; + long sspseg:32; /* Offset to linkage control of previous + segment of stack. */ + long:32; + long sstcpt:32; /* Pointer to task common address block. */ + long sscsnm; /* Private control structure number for + microtasking. */ + long ssusr1; /* Reserved for user. */ + long ssusr2; /* Reserved for user. */ + long sstpid; /* Process ID for pid based multi-tasking. */ + long ssgvup; /* Pointer to multitasking thread giveup. */ + long sscray[7]; /* Reserved for Cray Research. */ + long ssa0; + long ssa1; + long ssa2; + long ssa3; + long ssa4; + long ssa5; + long ssa6; + long ssa7; + long sss0; + long sss1; + long sss2; + long sss3; + long sss4; + long sss5; + long sss6; + long sss7; + }; + +#else /* CRAY2 */ +/* The following structure defines the vector of words + returned by the STKSTAT library routine. */ +struct stk_stat + { + long now; /* Current total stack size. */ + long maxc; /* Amount of contiguous space which would + be required to satisfy the maximum + stack demand to date. */ + long high_water; /* Stack high-water mark. */ + long overflows; /* Number of stack overflow ($STKOFEN) calls. */ + long hits; /* Number of internal buffer hits. */ + long extends; /* Number of block extensions. */ + long stko_mallocs; /* Block allocations by $STKOFEN. */ + long underflows; /* Number of stack underflow calls ($STKRETN). */ + long stko_free; /* Number of deallocations by $STKRETN. */ + long stkm_free; /* Number of deallocations by $STKMRET. */ + long segments; /* Current number of stack segments. */ + long maxs; /* Maximum number of stack segments so far. */ + long pad_size; /* Stack pad size. */ + long current_address; /* Current stack segment address. */ + long current_size; /* Current stack segment size. This + number is actually corrupted by STKSTAT to + include the fifteen word trailer area. */ + long initial_address; /* Address of initial segment. */ + long initial_size; /* Size of initial segment. */ + }; + +/* The following structure describes the data structure which trails + any stack segment. I think that the description in 'asdef' is + out of date. I only describe the parts that I am sure about. */ + +struct stk_trailer + { + long this_address; /* Address of this block. */ + long this_size; /* Size of this block (does not include + this trailer). */ + long unknown2; + long unknown3; + long link; /* Address of trailer block of previous + segment. */ + long unknown5; + long unknown6; + long unknown7; + long unknown8; + long unknown9; + long unknown10; + long unknown11; + long unknown12; + long unknown13; + long unknown14; + }; + +#endif /* CRAY2 */ +#endif /* not CRAY_STACK */ + +#ifdef CRAY2 +/* Determine a "stack measure" for an arbitrary ADDRESS. + I doubt that "lint" will like this much. */ + +static long +i00afunc (long *address) +{ + struct stk_stat status; + struct stk_trailer *trailer; + long *block, size; + long result = 0; + + /* We want to iterate through all of the segments. The first + step is to get the stack status structure. We could do this + more quickly and more directly, perhaps, by referencing the + $LM00 common block, but I know that this works. */ + + STKSTAT (&status); + + /* Set up the iteration. */ + + trailer = (struct stk_trailer *) (status.current_address + + status.current_size + - 15); + + /* There must be at least one stack segment. Therefore it is + a fatal error if "trailer" is null. */ + + if (trailer == 0) + abort (); + + /* Discard segments that do not contain our argument address. */ + + while (trailer != 0) + { + block = (long *) trailer->this_address; + size = trailer->this_size; + if (block == 0 || size == 0) + abort (); + trailer = (struct stk_trailer *) trailer->link; + if ((block <= address) && (address < (block + size))) + break; + } + + /* Set the result to the offset in this segment and add the sizes + of all predecessor segments. */ + + result = address - block; + + if (trailer == 0) + { + return result; + } + + do + { + if (trailer->this_size <= 0) + abort (); + result += trailer->this_size; + trailer = (struct stk_trailer *) trailer->link; + } + while (trailer != 0); + + /* We are done. Note that if you present a bogus address (one + not in any segment), you will get a different number back, formed + from subtracting the address of the first block. This is probably + not what you want. */ + + return (result); +} + +#else /* not CRAY2 */ +/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP. + Determine the number of the cell within the stack, + given the address of the cell. The purpose of this + routine is to linearize, in some sense, stack addresses + for alloca. */ + +static long +i00afunc (long address) +{ + long stkl = 0; + + long size, pseg, this_segment, stack; + long result = 0; + + struct stack_segment_linkage *ssptr; + + /* Register B67 contains the address of the end of the + current stack segment. If you (as a subprogram) store + your registers on the stack and find that you are past + the contents of B67, you have overflowed the segment. + + B67 also points to the stack segment linkage control + area, which is what we are really interested in. */ + + stkl = CRAY_STACKSEG_END (); + ssptr = (struct stack_segment_linkage *) stkl; + + /* If one subtracts 'size' from the end of the segment, + one has the address of the first word of the segment. + + If this is not the first segment, 'pseg' will be + nonzero. */ + + pseg = ssptr->sspseg; + size = ssptr->sssize; + + this_segment = stkl - size; + + /* It is possible that calling this routine itself caused + a stack overflow. Discard stack segments which do not + contain the target address. */ + + while (!(this_segment <= address && address <= stkl)) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl); +#endif + if (pseg == 0) + break; + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + this_segment = stkl - size; + } + + result = address - this_segment; + + /* If you subtract pseg from the current end of the stack, + you get the address of the previous stack segment's end. + This seems a little convoluted to me, but I'll bet you save + a cycle somewhere. */ + + while (pseg != 0) + { +#ifdef DEBUG_I00AFUNC + fprintf (stderr, "%011o %011o\n", pseg, size); +#endif + stkl = stkl - pseg; + ssptr = (struct stack_segment_linkage *) stkl; + size = ssptr->sssize; + pseg = ssptr->sspseg; + result += size; + } + return (result); +} + +#endif /* not CRAY2 */ +#endif /* CRAY */ + +#endif /* no alloca */ +#endif /* not GCC version 2 */ +#endif /* HAVE_ALLOCA */ + +/* + * 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/build-defs.h.in b/main/build-defs.h.in new file mode 100644 index 0000000..aa1fbf0 --- /dev/null +++ b/main/build-defs.h.in @@ -0,0 +1,91 @@ +/* -*- C -*- + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2007 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: Stig Sæther Bakken | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#define CONFIGURE_COMMAND "@CONFIGURE_COMMAND@" +#define PHP_ADA_INCLUDE "" +#define PHP_ADA_LFLAGS "" +#define PHP_ADA_LIBS "" +#define PHP_APACHE_INCLUDE "" +#define PHP_APACHE_TARGET "" +#define PHP_FHTTPD_INCLUDE "" +#define PHP_FHTTPD_LIB "" +#define PHP_FHTTPD_TARGET "" +#define PHP_CFLAGS "@CFLAGS@" +#define PHP_DBASE_LIB "" +#define PHP_BUILD_DEBUG "@DEBUG_CFLAGS@" +#define PHP_GDBM_INCLUDE "" +#define PHP_IBASE_INCLUDE "" +#define PHP_IBASE_LFLAGS "" +#define PHP_IBASE_LIBS "" +#define PHP_IFX_INCLUDE "" +#define PHP_IFX_LFLAGS "" +#define PHP_IFX_LIBS "" +#define PHP_INSTALL_IT "@INSTALL_IT@" +#define PHP_IODBC_INCLUDE "" +#define PHP_IODBC_LFLAGS "" +#define PHP_IODBC_LIBS "" +#define PHP_MSQL_INCLUDE "" +#define PHP_MSQL_LFLAGS "" +#define PHP_MSQL_LIBS "" +#define PHP_MYSQL_INCLUDE "@MYSQL_INCLUDE@" +#define PHP_MYSQL_LIBS "@MYSQL_LIBS@" +#define PHP_MYSQL_TYPE "@MYSQL_MODULE_TYPE@" +#define PHP_ODBC_INCLUDE "@ODBC_INCLUDE@" +#define PHP_ODBC_LFLAGS "@ODBC_LFLAGS@" +#define PHP_ODBC_LIBS "@ODBC_LIBS@" +#define PHP_ODBC_TYPE "@ODBC_TYPE@" +#define PHP_OCI8_SHARED_LIBADD "@OCI8_SHARED_LIBADD@" +#define PHP_OCI8_DIR "@OCI8_DIR@" +#define PHP_OCI8_ORACLE_VERSION "@OCI8_ORACLE_VERSION@" +#define PHP_ORACLE_SHARED_LIBADD "@ORACLE_SHARED_LIBADD@" +#define PHP_ORACLE_DIR "@ORACLE_DIR@" +#define PHP_ORACLE_VERSION "@ORACLE_VERSION@" +#define PHP_PGSQL_INCLUDE "" +#define PHP_PGSQL_LFLAGS "" +#define PHP_PGSQL_LIBS "" +#define PHP_PROG_SENDMAIL "@PROG_SENDMAIL@" +#define PHP_SOLID_INCLUDE "" +#define PHP_SOLID_LIBS "" +#define PHP_EMPRESS_INCLUDE "" +#define PHP_EMPRESS_LIBS "" +#define PHP_SYBASE_INCLUDE "" +#define PHP_SYBASE_LFLAGS "" +#define PHP_SYBASE_LIBS "" +#define PHP_DBM_TYPE "" +#define PHP_DBM_LIB "" +#define PHP_LDAP_LFLAGS "" +#define PHP_LDAP_INCLUDE "" +#define PHP_LDAP_LIBS "" +#define PHP_BIRDSTEP_INCLUDE "" +#define PHP_BIRDSTEP_LIBS "" +#define PEAR_INSTALLDIR "@EXPANDED_PEAR_INSTALLDIR@" +#define PHP_INCLUDE_PATH "@INCLUDE_PATH@" +#define PHP_EXTENSION_DIR "@EXPANDED_EXTENSION_DIR@" +#define PHP_PREFIX "@prefix@" +#define PHP_BINDIR "@EXPANDED_BINDIR@" +#define PHP_SBINDIR "@EXPANDED_SBINDIR@" +#define PHP_MANDIR "@EXPANDED_MANDIR@" +#define PHP_LIBDIR "@EXPANDED_LIBDIR@" +#define PHP_DATADIR "@EXPANDED_DATADIR@" +#define PHP_SYSCONFDIR "@EXPANDED_SYSCONFDIR@" +#define PHP_LOCALSTATEDIR "@EXPANDED_LOCALSTATEDIR@" +#define PHP_CONFIG_FILE_PATH "@EXPANDED_PHP_CONFIG_FILE_PATH@" +#define PHP_CONFIG_FILE_SCAN_DIR "@EXPANDED_PHP_CONFIG_FILE_SCAN_DIR@" +#define PHP_SHLIB_SUFFIX "@SHLIB_DL_SUFFIX_NAME@" diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c new file mode 100644 index 0000000..6f11cf3 --- /dev/null +++ b/main/fopen_wrappers.c @@ -0,0 +1,817 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jim Winstead | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* {{{ includes + */ +#include "php.h" +#include "php_globals.h" +#include "SAPI.h" + +#include +#include +#include +#include +#include +#include + +#ifdef PHP_WIN32 +#define O_RDONLY _O_RDONLY +#include "win32/param.h" +#else +#include +#endif + +#include "ext/standard/head.h" +#include "ext/standard/php_standard.h" +#include "zend_compile.h" +#include "php_network.h" + +#if HAVE_PWD_H +#include +#endif + +#include +#if HAVE_SYS_SOCKET_H +#include +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifdef PHP_WIN32 +#include +#elif defined(NETWARE) && defined(USE_WINSOCK) +#include +#else +#include +#include +#if HAVE_ARPA_INET_H +#include +#endif +#endif + +#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) +#undef AF_UNIX +#endif + +#if defined(AF_UNIX) +#include +#endif +/* }}} */ + +/* {{{ OnUpdateBaseDir +Allows any change to open_basedir setting in during Startup and Shutdown events, +or a tightening during activation/runtime/deactivation */ +PHPAPI ZEND_INI_MH(OnUpdateBaseDir) +{ + char **p, *pathbuf, *ptr, *end; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + p = (char **) (base + (size_t) mh_arg1); + + if (stage == PHP_INI_STAGE_STARTUP || stage == PHP_INI_STAGE_SHUTDOWN || stage == PHP_INI_STAGE_ACTIVATE || stage == PHP_INI_STAGE_DEACTIVATE) { + /* We're in a PHP_INI_SYSTEM context, no restrictions */ + *p = new_value; + return SUCCESS; + } + + /* Otherwise we're in runtime */ + if (!*p || !**p) { + /* open_basedir not set yet, go ahead and give it a value */ + *p = new_value; + return SUCCESS; + } + + /* Shortcut: When we have a open_basedir and someone tries to unset, we know it'll fail */ + if (!new_value || !*new_value) { + return FAILURE; + } + + /* Is the proposed open_basedir at least as restrictive as the current setting? */ + ptr = pathbuf = estrdup(new_value); + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + if (php_check_open_basedir_ex(ptr, 0 TSRMLS_CC) != 0) { + /* At least one portion of this open_basedir is less restrictive than the prior one, FAIL */ + efree(pathbuf); + return FAILURE; + } + ptr = end; + } + efree(pathbuf); + + /* Everything checks out, set it */ + *p = new_value; + + return SUCCESS; +} +/* }}} */ + +/* {{{ php_check_specific_open_basedir + When open_basedir is not NULL, check if the given filename is located in + open_basedir. Returns -1 if error or not in the open_basedir, else 0. + When open_basedir is NULL, always return 0. +*/ +PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC) +{ + char resolved_name[MAXPATHLEN]; + char resolved_basedir[MAXPATHLEN]; + char local_open_basedir[MAXPATHLEN]; + char path_tmp[MAXPATHLEN]; + char *path_file; + int resolved_basedir_len; + int resolved_name_len; + int path_len; + int nesting_level = 0; + + /* Special case basedir==".": Use script-directory */ + if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) { + /* Else use the unmodified path */ + strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir)); + } + + path_len = strlen(path); + if (path_len > (MAXPATHLEN - 1)) { + /* empty and too long paths are invalid */ + return -1; + } + + /* normalize and expand path */ + if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) { + return -1; + } + + path_len = strlen(resolved_name); + memcpy(path_tmp, resolved_name, path_len + 1); /* safe */ + + while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) { +#if defined(PHP_WIN32) || defined(HAVE_SYMLINK) +#if defined(PHP_WIN32) + if (EG(windows_version_info).dwMajorVersion > 5) { +#endif + if (nesting_level == 0) { + int ret; + char buf[MAXPATHLEN]; + + ret = php_sys_readlink(path_tmp, buf, MAXPATHLEN - 1); + if (ret < 0) { + /* not a broken symlink, move along.. */ + } else { + /* put the real path into the path buffer */ + memcpy(path_tmp, buf, ret); + path_tmp[ret] = '\0'; + } + } +#if defined(PHP_WIN32) + } +#endif +#endif + +#if defined(PHP_WIN32) || defined(NETWARE) + path_file = strrchr(path_tmp, DEFAULT_SLASH); + if (!path_file) { + path_file = strrchr(path_tmp, '/'); + } +#else + path_file = strrchr(path_tmp, DEFAULT_SLASH); +#endif + if (!path_file) { + /* none of the path components exist. definitely not in open_basedir.. */ + return -1; + } else { + path_len = path_file - path_tmp + 1; +#if defined(PHP_WIN32) || defined(NETWARE) + if (path_len > 1 && path_tmp[path_len - 2] == ':') { + if (path_len != 3) { + return -1; + } + /* this is c:\ */ + path_tmp[path_len] = '\0'; + } else { + path_tmp[path_len - 1] = '\0'; + } +#else + path_tmp[path_len - 1] = '\0'; +#endif + } + nesting_level++; + } + + /* Resolve open_basedir to resolved_basedir */ + if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) { + /* Handler for basedirs that end with a / */ + resolved_basedir_len = strlen(resolved_basedir); +#if defined(PHP_WIN32) || defined(NETWARE) + if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR || basedir[strlen(basedir) - 1] == '/') { +#else + if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) { +#endif + if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { + resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR; + resolved_basedir[++resolved_basedir_len] = '\0'; + } + } else { + resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR; + resolved_basedir[resolved_basedir_len] = '\0'; + } + + resolved_name_len = strlen(resolved_name); + if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) { + if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) { + resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR; + resolved_name[++resolved_name_len] = '\0'; + } + } + + /* Check the path */ +#if defined(PHP_WIN32) || defined(NETWARE) + if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { +#else + if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) { +#endif + if (resolved_name_len > resolved_basedir_len && + resolved_name[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) { + return -1; + } else { + /* File is in the right directory */ + return 0; + } + } else { + /* /openbasedir/ and /openbasedir are the same directory */ + if (resolved_basedir_len == (resolved_name_len + 1) && resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) { +#if defined(PHP_WIN32) || defined(NETWARE) + if (strncasecmp(resolved_basedir, resolved_name, resolved_name_len) == 0) { +#else + if (strncmp(resolved_basedir, resolved_name, resolved_name_len) == 0) { +#endif + return 0; + } + } + return -1; + } + } else { + /* Unable to resolve the real path, return -1 */ + return -1; + } +} +/* }}} */ + +PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC) +{ + return php_check_open_basedir_ex(path, 1 TSRMLS_CC); +} + +/* {{{ php_check_open_basedir + */ +PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC) +{ + /* Only check when open_basedir is available */ + if (PG(open_basedir) && *PG(open_basedir)) { + char *pathbuf; + char *ptr; + char *end; + + /* Check if the path is too long so we can give a more useful error + * message. */ + if (strlen(path) > (MAXPATHLEN - 1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path); + errno = EINVAL; + return -1; + } + + pathbuf = estrdup(PG(open_basedir)); + + ptr = pathbuf; + + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + + if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) { + efree(pathbuf); + return 0; + } + + ptr = end; + } + if (warn) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir)); + } + efree(pathbuf); + errno = EPERM; /* we deny permission to open it */ + 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) +{ + FILE *fp; + + if (php_check_open_basedir((char *)path TSRMLS_CC)) { + return NULL; + } + fp = VCWD_FOPEN(path, mode); + if (fp && opened_path) { + *opened_path = expand_filepath_with_mode(path, NULL, NULL, 0, CWD_EXPAND TSRMLS_CC); + } + return fp; +} +/* }}} */ + +/* {{{ php_fopen_primary_script + */ +PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC) +{ + 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 + if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) { + char *s = strchr(path_info + 2, '/'); + + if (s) { /* if there is no path name after the file, do not bother */ + char user[32]; /* to try open the directory */ + struct passwd *pw; +#if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX) + struct passwd pwstruc; + long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX); + char *pwbuf; + + if (pwbuflen < 1) { + return FAILURE; + } + + pwbuf = emalloc(pwbuflen); +#endif + length = s - (path_info + 2); + if (length > (int)sizeof(user) - 1) { + length = sizeof(user) - 1; + } + memcpy(user, path_info + 2, length); + user[length] = '\0'; +#if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX) + if (getpwnam_r(user, &pwstruc, pwbuf, pwbuflen, &pw)) { + efree(pwbuf); + return FAILURE; + } +#else + pw = getpwnam(user); +#endif + if (pw && pw->pw_dir) { + spprintf(&filename, 0, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR, PG(user_dir), PHP_DIR_SEPARATOR, s + 1); /* Safe */ + } else { + filename = SG(request_info).path_translated; + } +#if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX) + efree(pwbuf); +#endif + } + } else +#endif + if (PG(doc_root) && path_info && (length = strlen(PG(doc_root))) && + IS_ABSOLUTE_PATH(PG(doc_root), length)) { + int path_len = strlen(path_info); + filename = emalloc(length + path_len + 2); + if (filename) { + memcpy(filename, PG(doc_root), length); + if (!IS_SLASH(filename[length - 1])) { /* length is never 0 */ + filename[length++] = PHP_DIR_SEPARATOR; + } + if (IS_SLASH(path_info[0])) { + length--; + } + strncpy(filename + length, path_info, path_len + 1); + } + } else { + filename = SG(request_info).path_translated; + } + + + if (filename) { + resolved_path = zend_resolve_path(filename, strlen(filename) TSRMLS_CC); + } + + if (!resolved_path) { + if (SG(request_info).path_translated != filename) { + STR_FREE(filename); + } + /* we have to free SG(request_info).path_translated here because + * php_destroy_request_info assumes that it will get + * freed when the include_names hash is emptied, but + * we're not adding it in this case */ + STR_FREE(SG(request_info).path_translated); + SG(request_info).path_translated = NULL; + return FAILURE; + } + efree(resolved_path); + + 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); + } + STR_FREE(SG(request_info).path_translated); /* for same reason as above */ + SG(request_info).path_translated = NULL; + return FAILURE; + } + 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; + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ php_resolve_path + * Returns the realpath for given filename according to include path + */ +PHPAPI char *php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC) +{ + char resolved_path[MAXPATHLEN]; + char trypath[MAXPATHLEN]; + const char *ptr, *end, *p; + char *actual_path; + php_stream_wrapper *wrapper; + + if (!filename || CHECK_NULL_PATH(filename, filename_length)) { + return NULL; + } + + /* Don't resolve paths which contain protocol (except of file://) */ + for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) { + wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (wrapper == &php_plain_files_wrapper) { + if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { + return estrdup(resolved_path); + } + } + return NULL; + } + + if ((*filename == '.' && + (IS_SLASH(filename[1]) || + ((filename[1] == '.') && IS_SLASH(filename[2])))) || + IS_ABSOLUTE_PATH(filename, filename_length) || + !path || + !*path) { + if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) { + return estrdup(resolved_path); + } else { + return NULL; + } + } + + ptr = path; + while (ptr && *ptr) { + /* Check for stream wrapper */ + int is_stream_wrapper = 0; + + for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) { + /* .:// or ..:// is not a stream wrapper */ + if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) { + p += 3; + is_stream_wrapper = 1; + } + } + end = strchr(p, DEFAULT_DIR_SEPARATOR); + if (end) { + if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) { + ptr = end + 1; + continue; + } + memcpy(trypath, ptr, end-ptr); + trypath[end-ptr] = '/'; + memcpy(trypath+(end-ptr)+1, filename, filename_length+1); + ptr = end+1; + } else { + int len = strlen(ptr); + + if (len + 1 + filename_length + 1 >= MAXPATHLEN) { + break; + } + memcpy(trypath, ptr, len); + trypath[len] = '/'; + memcpy(trypath+len+1, filename, filename_length+1); + ptr = NULL; + } + actual_path = trypath; + if (is_stream_wrapper) { + wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (!wrapper) { + continue; + } else if (wrapper != &php_plain_files_wrapper) { + if (wrapper->wops->url_stat) { + php_stream_statbuf ssb; + + if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) { + return estrdup(trypath); + } + } + continue; + } + } + if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { + return estrdup(resolved_path); + } + } /* end provided path */ + + /* check in calling scripts' current working directory as a fall back case + */ + if (zend_is_executing(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])); + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) { + memcpy(trypath, exec_fname, exec_fname_length + 1); + memcpy(trypath+exec_fname_length + 1, filename, filename_length+1); + actual_path = trypath; + + /* Check for stream wrapper */ + for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) { + wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); + if (!wrapper) { + return NULL; + } else if (wrapper != &php_plain_files_wrapper) { + if (wrapper->wops->url_stat) { + php_stream_statbuf ssb; + + if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) { + return estrdup(trypath); + } + } + return NULL; + } + } + + if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) { + return estrdup(resolved_path); + } + } + } + + return NULL; +} +/* }}} */ + +/* {{{ php_fopen_with_path + * Tries to open a file with a PATH-style list of directories. + * If the filename starts with "." or "/", the path is ignored. + */ +PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC) +{ + char *pathbuf, *ptr, *end; + const char *exec_fname; + char trypath[MAXPATHLEN]; + FILE *fp; + int path_length; + int filename_length; + int exec_fname_length; + + if (opened_path) { + *opened_path = NULL; + } + + if (!filename) { + return NULL; + } + + filename_length = strlen(filename); + + /* Relative path open */ + if ((*filename == '.') + /* Absolute path open */ + || IS_ABSOLUTE_PATH(filename, filename_length) + || (!path || (path && !*path)) + ) { + return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC); + } + + /* check in provided path */ + /* append the calling scripts' current working directory + * as a fall back case + */ + if (zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + path_length = strlen(path); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if ((exec_fname && exec_fname[0] == '[') || exec_fname_length <= 0) { + /* [no active file] or no path */ + pathbuf = estrdup(path); + } else { + pathbuf = (char *) emalloc(exec_fname_length + path_length + 1 + 1); + memcpy(pathbuf, path, path_length); + pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; + memcpy(pathbuf + path_length + 1, exec_fname, exec_fname_length); + pathbuf[path_length + exec_fname_length + 1] = '\0'; + } + } else { + pathbuf = estrdup(path); + } + + ptr = pathbuf; + + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + 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); + } + fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC); + if (fp) { + efree(pathbuf); + return fp; + } + ptr = end; + } /* end provided path */ + + efree(pathbuf); + return NULL; +} +/* }}} */ + +/* {{{ php_strip_url_passwd + */ +PHPAPI char *php_strip_url_passwd(char *url) +{ + register char *p, *url_start; + + if (url == NULL) { + return ""; + } + + p = url; + + while (*p) { + if (*p == ':' && *(p + 1) == '/' && *(p + 2) == '/') { + /* found protocol */ + url_start = p = p + 3; + + while (*p) { + if (*p == '@') { + int i; + + for (i = 0; i < 3 && url_start < p; i++, url_start++) { + *url_start = '.'; + } + for (; *p; p++) { + *url_start++ = *p; + } + *url_start=0; + break; + } + p++; + } + return url; + } + p++; + } + return url; +} +/* }}} */ + +/* {{{ expand_filepath + */ +PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC) +{ + return expand_filepath_ex(filepath, real_path, NULL, 0 TSRMLS_CC); +} +/* }}} */ + +/* {{{ expand_filepath_ex + */ +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; + + if (!filepath[0]) { + return NULL; + } else if (IS_ABSOLUTE_PATH(filepath, strlen(filepath))) { + cwd[0] = '\0'; + } else { + const char *iam = SG(request_info).path_translated; + const char *result; + if (relative_to) { + if (relative_to_len > MAXPATHLEN-1U) { + return NULL; + } + result = relative_to; + memcpy(cwd, relative_to, relative_to_len+1U); + } else { + result = VCWD_GETCWD(cwd, MAXPATHLEN); + } + + if (!result && (iam != filepath)) { + int fdtest = -1; + + fdtest = VCWD_OPEN(filepath, O_RDONLY); + if (fdtest != -1) { + /* return a relative file path if for any reason + * we cannot cannot getcwd() and the requested, + * relatively referenced file is accessible */ + copy_len = strlen(filepath) > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : strlen(filepath); + real_path = estrndup(filepath, copy_len); + close(fdtest); + return real_path; + } else { + cwd[0] = '\0'; + } + } else if (!result) { + cwd[0] = '\0'; + } + } + + new_state.cwd = strdup(cwd); + new_state.cwd_length = strlen(cwd); + + if (virtual_file_ex(&new_state, filepath, NULL, realpath_mode TSRMLS_CC)) { + free(new_state.cwd); + return NULL; + } + + if (real_path) { + copy_len = new_state.cwd_length > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : new_state.cwd_length; + memcpy(real_path, new_state.cwd, copy_len); + real_path[copy_len] = '\0'; + } else { + real_path = estrndup(new_state.cwd, new_state.cwd_length); + } + free(new_state.cwd); + + return real_path; +} +/* }}} */ + +/* + * 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/fopen_wrappers.h b/main/fopen_wrappers.h new file mode 100644 index 0000000..ae0001f --- /dev/null +++ b/main/fopen_wrappers.h @@ -0,0 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Jim Winstead | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef FOPEN_WRAPPERS_H +#define FOPEN_WRAPPERS_H + +BEGIN_EXTERN_C() +#include "php_globals.h" +#include "php_ini.h" + +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_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 +#define OPENBASEDIR_CHECKPATH(filename) \ + php_check_open_basedir(filename TSRMLS_CC) +#endif +/* }}} */ + +PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC); + +PHPAPI char *php_resolve_path(const char *filename, int filename_len, const char *path TSRMLS_DC); + +PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC); + +PHPAPI char *php_strip_url_passwd(char *path); + +PHPAPI ZEND_INI_MH(OnUpdateBaseDir); +END_EXTERN_C() + +#endif +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/getopt.c b/main/getopt.c new file mode 100644 index 0000000..591c8c7 --- /dev/null +++ b/main/getopt.c @@ -0,0 +1,199 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include "php_getopt.h" + +#define OPTERRCOLON (1) +#define OPTERRNF (2) +#define OPTERRARG (3) + +static int php_opt_error(int argc, char * const *argv, int oint, int optchr, int err, int show_err) /* {{{ */ +{ + if (show_err) + { + fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1); + switch(err) + { + case OPTERRCOLON: + fprintf(stderr, ": in flags\n"); + break; + case OPTERRNF: + fprintf(stderr, "option not found %c\n", argv[oint][optchr]); + break; + case OPTERRARG: + fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]); + break; + default: + fprintf(stderr, "unknown\n"); + break; + } + } + return('?'); +} +/* }}} */ + +PHPAPI int php_optidx = -1; + +PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **optarg, int *optind, int show_err, int arg_start) /* {{{ */ +{ + static int optchr = 0; + static int dash = 0; /* have already seen the - */ + + php_optidx = -1; + + if (*optind >= argc) { + return(EOF); + } + if (!dash) { + if ((argv[*optind][0] != '-')) { + return(EOF); + } else { + if (!argv[*optind][1]) + { + /* + * use to specify stdin. Need to let pgm process this and + * the following args + */ + return(EOF); + } + } + } + if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) { + char *pos; + int arg_end = strlen(argv[*optind])-1; + + /* '--' indicates end of args if not followed by a known long option name */ + if (argv[*optind][2] == '\0') { + (*optind)++; + return(EOF); + } + + arg_start = 2; + + /* Check for = */ + if ((pos = php_memnstr(&argv[*optind][arg_start], "=", 1, argv[*optind]+arg_end)) != NULL) { + arg_end = pos-&argv[*optind][arg_start]; + arg_start++; + } else { + arg_end--; + } + + while (1) { + php_optidx++; + if (opts[php_optidx].opt_char == '-') { + (*optind)++; + return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + } else if (opts[php_optidx].opt_name && !strncmp(&argv[*optind][2], opts[php_optidx].opt_name, arg_end) && arg_end == strlen(opts[php_optidx].opt_name)) { + break; + } + } + + optchr = 0; + dash = 0; + arg_start += strlen(opts[php_optidx].opt_name); + } else { + if (!dash) { + dash = 1; + optchr = 1; + } + /* Check if the guy tries to do a -: kind of flag */ + if (argv[*optind][optchr] == ':') { + dash = 0; + (*optind)++; + return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err)); + } + arg_start = 1 + optchr; + } + if (php_optidx < 0) { + while (1) { + php_optidx++; + if (opts[php_optidx].opt_char == '-') { + int errind = *optind; + int errchr = optchr; + + if (!argv[*optind][optchr+1]) { + dash = 0; + (*optind)++; + } else { + optchr++; + arg_start++; + } + return(php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err)); + } else if (argv[*optind][optchr] == opts[php_optidx].opt_char) { + break; + } + } + } + if (opts[php_optidx].need_param) { + /* Check for cases where the value of the argument + is in the form - , -= or - */ + dash = 0; + if (!argv[*optind][arg_start]) { + (*optind)++; + if (*optind == argc) { + /* Was the value required or is it optional? */ + if (opts[php_optidx].need_param == 1) { + return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + } + /* Optional value is not supported with - style */ + } else if (opts[php_optidx].need_param == 1) { + *optarg = argv[(*optind)++]; + } + } else if (argv[*optind][arg_start] == '=') { + arg_start++; + *optarg = &argv[*optind][arg_start]; + (*optind)++; + } else { + *optarg = &argv[*optind][arg_start]; + (*optind)++; + } + return opts[php_optidx].opt_char; + } else { + /* multiple options specified as one (exclude long opts) */ + if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) { + if (!argv[*optind][optchr+1]) + { + dash = 0; + (*optind)++; + } else { + optchr++; + } + } else { + (*optind)++; + } + return opts[php_optidx].opt_char; + } + assert(0); + return(0); /* never reached */ +} +/* }}} */ + +/* + * 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/internal_functions.c.in b/main/internal_functions.c.in new file mode 100644 index 0000000..f1e66b1 --- /dev/null +++ b/main/internal_functions.c.in @@ -0,0 +1,48 @@ +/* -*- C -*- + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2007 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_main.h" +#include "zend_modules.h" +#include "zend_compile.h" +#include +#include +#include + +@EXT_INCLUDE_CODE@ + +static zend_module_entry *php_builtin_extensions[] = { +@EXT_MODULE_PTRS@ +}; + +#define EXTCOUNT (sizeof(php_builtin_extensions)/sizeof(zend_module_entry *)) + +PHPAPI int php_register_internal_extensions(TSRMLS_D) +{ + return php_register_extensions(php_builtin_extensions, EXTCOUNT TSRMLS_CC); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/internal_functions_nw.c b/main/internal_functions_nw.c new file mode 100644 index 0000000..07aec9a --- /dev/null +++ b/main/internal_functions_nw.c @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Modified for NetWare: Novell, Inc. | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* {{{ includes + */ +#include "php.h" +#include "php_main.h" +#include "zend_modules.h" +#include "zend_compile.h" +#include +#include +#include + +#include "ext/bcmath/php_bcmath.h" +#include "ext/gd/php_gd.h" +#include "ext/standard/dl.h" +#include "ext/standard/file.h" +#include "ext/standard/fsock.h" +#include "ext/standard/head.h" +#include "ext/standard/pack.h" +#include "ext/standard/php_browscap.h" +/*#include "ext/standard/php_crypt.h"*/ +#include "ext/standard/php_dir.h" +#include "ext/standard/php_filestat.h" +#include "ext/standard/php_mail.h" +/*#include "ext/standard/php_ext_syslog.h"*/ +#include "ext/standard/php_standard.h" +#include "ext/standard/php_lcg.h" +#include "ext/standard/php_array.h" +#include "ext/standard/php_assert.h" +#include "ext/calendar/php_calendar.h" +/*#include "ext/com/php_COM.h" +#include "ext/com/php_VARIANT.h"*/ +#include "ext/ftp/php_ftp.h" +#include "ext/standard/reg.h" +#include "ext/pcre/php_pcre.h" +/*#include "ext/odbc/php_odbc.h"*/ /* Commented out for now */ +#include "ext/session/php_session.h" +/*#include "ext/xml/php_xml.h" +#include "ext/wddx/php_wddx.h" +#include "ext/mysql/php_mysql.h"*/ /* Commented out for now */ +/* }}} */ + +/* {{{ php_builtin_extensions[] + */ +static zend_module_entry *php_builtin_extensions[] = { + phpext_standard_ptr, +#if HAVE_BCMATH + phpext_bcmath_ptr, +#endif + phpext_calendar_ptr, +/* COM_module_ptr,*/ + phpext_ftp_ptr, +#if defined(MBSTR_ENC_TRANS) + phpext_mbstring_ptr, +#endif +/* phpext_mysql_ptr,*/ /* Commented out for now */ +/* phpext_odbc_ptr, */ /* Commented out for now */ + phpext_pcre_ptr, + phpext_session_ptr, +/* phpext_xml_ptr, + phpext_wddx_ptr */ /* Commented out for now */ +}; +/* }}} */ + +#define EXTCOUNT (sizeof(php_builtin_extensions)/sizeof(zend_module_entry *)) + +PHPAPI int php_register_internal_extensions(TSRMLS_D) +{ + return php_register_extensions(php_builtin_extensions, EXTCOUNT TSRMLS_CC); +} + + +/* + * 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/internal_functions_win32.c b/main/internal_functions_win32.c new file mode 100644 index 0000000..06cd5b2 --- /dev/null +++ b/main/internal_functions_win32.c @@ -0,0 +1,208 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* {{{ includes + */ +#include "php.h" +#include "php_main.h" +#include "zend_modules.h" +#include "zend_compile.h" +#include +#include +#include + +#ifndef ZEND_ENGINE_2 +#error HEAD does not work with ZendEngine1 anymore +#endif + +#include "ext/standard/dl.h" +#include "ext/standard/file.h" +#include "ext/standard/fsock.h" +#include "ext/standard/head.h" +#include "ext/standard/pack.h" +#include "ext/standard/php_browscap.h" +#include "ext/standard/php_crypt.h" +#include "ext/standard/php_dir.h" +#include "ext/standard/php_filestat.h" +#include "ext/standard/php_mail.h" +#include "ext/standard/php_ext_syslog.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/php_lcg.h" +#include "ext/standard/php_array.h" +#include "ext/standard/php_assert.h" +#include "ext/reflection/php_reflection.h" +#if HAVE_BCMATH +#include "ext/bcmath/php_bcmath.h" +#endif +#if HAVE_CALENDAR +#include "ext/calendar/php_calendar.h" +#endif +#if HAVE_CTYPE +#include "ext/ctype/php_ctype.h" +#endif +#if HAVE_DATE +#include "ext/date/php_date.h" +#endif +#if HAVE_FTP +#include "ext/ftp/php_ftp.h" +#endif +#if HAVE_ICONV +#include "ext/iconv/php_iconv.h" +#endif +#include "ext/standard/reg.h" +#if HAVE_PCRE || HAVE_BUNDLED_PCRE +#include "ext/pcre/php_pcre.h" +#endif +#if HAVE_UODBC +#include "ext/odbc/php_odbc.h" +#endif +#if HAVE_PHP_SESSION +#include "ext/session/php_session.h" +#endif +#if HAVE_MBSTRING +#include "ext/mbstring/mbstring.h" +#endif +#if HAVE_TOKENIZER +#include "ext/tokenizer/php_tokenizer.h" +#endif +#if HAVE_ZLIB +#include "ext/zlib/php_zlib.h" +#endif +#if HAVE_LIBXML +#include "ext/libxml/php_libxml.h" +#if HAVE_DOM +#include "ext/dom/php_dom.h" +#endif +#if HAVE_SIMPLEXML +#include "ext/simplexml/php_simplexml.h" +#endif +#endif +#if HAVE_XML +#include "ext/xml/php_xml.h" +#endif +#if HAVE_XML && HAVE_WDDX +#include "ext/wddx/php_wddx.h" +#endif +#ifdef HAVE_SQLITE +#include "ext/sqlite/php_sqlite.h" +#endif +#include "ext/com_dotnet/php_com_dotnet.h" +#ifdef HAVE_SPL +#include "ext/spl/php_spl.h" +#endif +#if HAVE_XML && HAVE_XMLREADER +#include "ext/xmlreader/php_xmlreader.h" +#endif +#if HAVE_XML && HAVE_XMLWRITER +#include "ext/xmlwriter/php_xmlwriter.h" +#endif +/* }}} */ + +/* {{{ php_builtin_extensions[] + */ +static zend_module_entry *php_builtin_extensions[] = { + phpext_standard_ptr +#if HAVE_BCMATH + ,phpext_bcmath_ptr +#endif +#if HAVE_CALENDAR + ,phpext_calendar_ptr +#endif + ,phpext_com_dotnet_ptr +#if HAVE_CTYPE + ,phpext_ctype_ptr +#endif +#if HAVE_DATE + ,phpext_date_ptr +#endif +#if HAVE_FTP + ,phpext_ftp_ptr +#endif +#if HAVE_HASH + ,phpext_hash_ptr +#endif +#if HAVE_ICONV + ,phpext_iconv_ptr +#endif +#if HAVE_MBSTRING + ,phpext_mbstring_ptr +#endif +#if HAVE_UODBC + ,phpext_odbc_ptr +#endif +#if HAVE_PCRE || HAVE_BUNDLED_PCRE + ,phpext_pcre_ptr +#endif + ,phpext_reflection_ptr +#if HAVE_PHP_SESSION + ,phpext_session_ptr +#endif +#if HAVE_TOKENIZER + ,phpext_tokenizer_ptr +#endif +#if HAVE_ZLIB + ,phpext_zlib_ptr +#endif +#if HAVE_LIBXML + ,phpext_libxml_ptr +#if HAVE_DOM + ,phpext_dom_ptr +#endif +#if HAVE_SIMPLEXML + ,phpext_simplexml_ptr +#endif +#endif +#if HAVE_XML + ,phpext_xml_ptr +#endif +#if HAVE_XML && HAVE_WDDX + ,phpext_wddx_ptr +#endif +#if HAVE_SQLITE + ,phpext_sqlite_ptr +#endif +#if HAVE_SPL + ,phpext_spl_ptr +#endif +#if HAVE_XML && HAVE_XMLREADER + ,phpext_xmlreader_ptr +#endif +#if HAVE_XML && HAVE_XMLWRITER + ,phpext_xmlwriter_ptr +#endif +}; +/* }}} */ + +#define EXTCOUNT (sizeof(php_builtin_extensions)/sizeof(zend_module_entry *)) + +PHPAPI int php_register_internal_extensions(TSRMLS_D) +{ + return php_register_extensions(php_builtin_extensions, EXTCOUNT TSRMLS_CC); +} + +/* + * 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/logos.h b/main/logos.h new file mode 100644 index 0000000..eb28107 --- /dev/null +++ b/main/logos.h @@ -0,0 +1,1080 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +#define CONTEXT_TYPE_IMAGE_GIF "Content-Type: image/gif" + +static const unsigned char zend_logo[] = { + 71, 73, 70, 56, 57, 97, 113, 0, 72, 0, + 213, 0, 0, 13, 13, 14, 1, 3, 6, 2, + 5, 9, 46, 68, 94, 21, 29, 39, 5, 15, + 26, 4, 10, 17, 29, 43, 58, 0, 1, 2, + 9, 25, 42, 38, 105, 171, 24, 67, 109, 13, + 36, 59, 10, 27, 45, 9, 25, 41, 35, 96, + 157, 32, 87, 142, 29, 79, 130, 26, 70, 114, + 20, 54, 87, 29, 77, 124, 10, 26, 42, 34, + 88, 141, 10, 24, 38, 11, 26, 41, 1, 2, + 3, 55, 80, 105, 45, 63, 81, 49, 53, 57, + 5, 15, 24, 9, 26, 42, 30, 85, 138, 33, + 92, 149, 26, 73, 117, 10, 28, 45, 32, 89, + 142, 30, 84, 134, 26, 72, 115, 15, 42, 67, + 23, 62, 99, 12, 32, 51, 7, 21, 33, 9, + 26, 41, 8, 23, 35, 7, 25, 37, 51, 58, + 63, 2, 4, 5, 25, 26, 26, 49, 50, 50, + 255, 102, 0, 255, 255, 255, 204, 204, 204, 199, + 199, 199, 191, 191, 191, 171, 171, 171, 146, 146, + 146, 115, 115, 115, 85, 85, 85, 60, 60, 60, + 55, 55, 55, 38, 38, 38, 7, 7, 7, 3, + 3, 3, 0, 0, 0, 44, 0, 0, 0, 0, + 113, 0, 72, 0, 0, 6, 255, 192, 153, 112, + 72, 44, 26, 143, 200, 164, 114, 121, 252, 49, + 159, 208, 168, 148, 248, 171, 58, 167, 210, 171, + 208, 170, 197, 122, 191, 70, 109, 23, 140, 236, + 138, 201, 232, 239, 121, 102, 221, 186, 217, 219, + 171, 83, 46, 110, 15, 207, 235, 180, 190, 124, + 135, 187, 229, 127, 127, 128, 112, 121, 108, 118, + 132, 123, 137, 77, 118, 120, 136, 115, 109, 117, + 85, 126, 147, 147, 128, 99, 138, 137, 99, 107, + 146, 146, 148, 133, 159, 125, 136, 152, 163, 151, + 135, 121, 144, 84, 157, 92, 169, 157, 111, 163, + 175, 176, 83, 151, 177, 180, 181, 161, 182, 184, + 185, 186, 187, 188, 189, 67, 54, 56, 58, 56, + 53, 190, 197, 88, 55, 57, 60, 63, 2, 43, + 2, 56, 198, 208, 74, 192, 58, 0, 63, 5, + 12, 11, 35, 35, 12, 47, 209, 222, 67, 53, + 201, 203, 34, 19, 20, 218, 231, 37, 63, 54, + 223, 222, 60, 2, 216, 231, 241, 231, 206, 76, + 193, 55, 236, 176, 63, 39, 242, 252, 35, 40, + 58, 75, 114, 8, 40, 240, 227, 25, 62, 76, + 60, 24, 244, 147, 55, 161, 202, 11, 24, 57, + 134, 17, 201, 241, 99, 130, 191, 130, 7, 21, + 225, 48, 176, 48, 30, 137, 5, 11, 38, 48, + 88, 81, 5, 198, 51, 138, 22, 181, 53, 52, + 152, 49, 141, 141, 31, 230, 58, 46, 60, 129, + 194, 74, 202, 115, 43, 91, 234, 1, 112, 83, + 102, 63, 255, 18, 38, 122, 226, 252, 145, 67, + 39, 153, 26, 47, 68, 248, 92, 186, 176, 97, + 81, 163, 88, 108, 188, 48, 80, 130, 169, 85, + 134, 68, 161, 74, 177, 1, 160, 0, 137, 171, + 96, 135, 62, 213, 186, 132, 171, 215, 176, 104, + 71, 52, 188, 71, 54, 9, 210, 179, 105, 195, + 166, 99, 219, 214, 198, 58, 34, 48, 12, 124, + 213, 38, 163, 175, 223, 191, 50, 22, 132, 216, + 139, 182, 132, 0, 30, 196, 250, 218, 37, 70, + 198, 198, 141, 199, 144, 31, 223, 253, 114, 163, + 10, 0, 97, 192, 126, 100, 59, 7, 24, 176, + 10, 20, 229, 210, 146, 48, 128, 88, 72, 223, + 28, 57, 38, 111, 197, 17, 163, 181, 235, 215, + 58, 116, 68, 100, 60, 5, 134, 136, 18, 19, + 80, 24, 168, 162, 48, 94, 95, 21, 6, 82, + 168, 80, 209, 215, 128, 1, 20, 39, 8, 95, + 37, 81, 160, 180, 105, 25, 177, 233, 62, 1, + 246, 186, 186, 245, 24, 209, 177, 84, 140, 71, + 97, 130, 114, 109, 33, 76, 48, 152, 176, 192, + 111, 135, 20, 222, 211, 54, 167, 61, 163, 111, + 246, 39, 55, 174, 203, 175, 254, 30, 74, 229, + 152, 62, 73, 132, 24, 60, 162, 47, 131, 14, + 12, 132, 224, 155, 95, 130, 145, 208, 89, 129, + 40, 116, 211, 25, 12, 58, 72, 135, 68, 124, + 214, 197, 38, 161, 14, 243, 73, 200, 222, 18, + 27, 161, 213, 215, 4, 159, 37, 199, 255, 25, + 96, 12, 116, 230, 223, 9, 11, 252, 32, 226, + 11, 13, 74, 35, 223, 132, 19, 174, 24, 27, + 75, 76, 192, 128, 66, 88, 126, 161, 160, 66, + 122, 124, 201, 112, 2, 3, 195, 125, 54, 65, + 95, 29, 4, 87, 227, 9, 126, 233, 192, 3, + 12, 125, 161, 232, 160, 93, 207, 184, 200, 98, + 139, 17, 74, 24, 197, 118, 96, 249, 7, 160, + 128, 31, 242, 72, 30, 72, 33, 244, 149, 2, + 122, 229, 201, 0, 224, 105, 60, 52, 216, 23, + 131, 14, 226, 96, 194, 1, 78, 74, 152, 195, + 13, 196, 208, 192, 90, 148, 177, 217, 7, 83, + 149, 50, 112, 136, 220, 94, 34, 22, 216, 159, + 12, 195, 101, 227, 37, 113, 50, 192, 0, 3, + 156, 238, 165, 72, 196, 11, 21, 178, 184, 228, + 117, 19, 66, 145, 225, 85, 53, 222, 72, 152, + 136, 42, 152, 32, 224, 111, 42, 8, 10, 40, + 161, 101, 222, 96, 67, 95, 61, 40, 58, 3, + 13, 55, 24, 112, 221, 101, 19, 226, 64, 131, + 17, 54, 64, 42, 229, 19, 50, 226, 249, 95, + 128, 127, 238, 23, 166, 113, 194, 105, 250, 103, + 160, 191, 18, 90, 232, 13, 59, 0, 249, 130, + 168, 56, 160, 198, 195, 10, 214, 161, 144, 1, + 131, 245, 21, 1, 33, 125, 177, 141, 181, 4, + 149, 76, 137, 232, 23, 3, 38, 132, 9, 216, + 9, 93, 126, 234, 105, 160, 34, 94, 208, 129, + 14, 7, 152, 112, 255, 2, 4, 40, 84, 80, + 157, 8, 30, 176, 154, 195, 133, 68, 200, 250, + 34, 124, 119, 102, 171, 109, 113, 157, 142, 16, + 238, 111, 160, 113, 58, 110, 167, 38, 252, 213, + 192, 193, 12, 72, 32, 193, 3, 15, 128, 192, + 128, 7, 175, 121, 32, 66, 7, 101, 230, 240, + 42, 18, 53, 216, 171, 131, 106, 73, 224, 32, + 0, 88, 221, 217, 216, 227, 200, 190, 234, 39, + 210, 112, 12, 36, 23, 158, 175, 254, 154, 160, + 105, 9, 5, 236, 88, 129, 7, 41, 83, 240, + 65, 195, 32, 56, 236, 128, 107, 14, 36, 208, + 64, 10, 24, 41, 161, 177, 181, 74, 84, 182, + 194, 4, 85, 45, 181, 239, 134, 11, 236, 165, + 31, 151, 95, 233, 199, 223, 8, 82, 27, 40, + 3, 9, 39, 144, 56, 24, 4, 92, 119, 61, + 129, 3, 96, 135, 221, 128, 8, 12, 0, 64, + 239, 47, 26, 59, 88, 52, 53, 227, 132, 182, + 208, 210, 128, 178, 76, 163, 12, 46, 135, 208, + 245, 221, 92, 3, 21, 54, 216, 9, 152, 208, + 48, 64, 72, 12, 237, 197, 52, 0, 152, 208, + 209, 111, 193, 141, 204, 239, 102, 104, 45, 96, + 128, 9, 20, 224, 45, 249, 215, 97, 51, 240, + 1, 8, 10, 152, 80, 20, 14, 56, 208, 213, + 38, 199, 181, 245, 214, 207, 111, 96, 130, 4, + 146, 72, 114, 135, 69, 194, 10, 38, 64, 0, + 130, 228, 120, 71, 240, 181, 8, 145, 67, 240, + 255, 128, 2, 44, 164, 0, 26, 2, 66, 180, + 9, 163, 23, 56, 20, 112, 184, 184, 242, 232, + 119, 224, 212, 127, 18, 56, 152, 95, 14, 252, + 8, 88, 129, 120, 255, 37, 216, 205, 17, 68, + 160, 192, 3, 24, 212, 64, 131, 198, 58, 92, + 12, 70, 101, 223, 125, 8, 172, 60, 218, 166, + 252, 149, 136, 41, 251, 21, 64, 249, 201, 117, + 221, 89, 202, 151, 131, 112, 251, 9, 211, 194, + 54, 161, 218, 88, 212, 128, 237, 128, 207, 35, + 15, 164, 144, 125, 65, 78, 141, 206, 3, 164, + 0, 245, 69, 0, 34, 248, 95, 10, 134, 68, + 2, 8, 244, 101, 71, 97, 3, 77, 237, 70, + 240, 0, 18, 248, 110, 15, 58, 80, 10, 63, + 48, 37, 55, 47, 129, 169, 128, 86, 10, 152, + 12, 132, 19, 166, 2, 36, 112, 132, 31, 20, + 83, 128, 28, 40, 3, 6, 52, 207, 116, 91, + 227, 218, 3, 218, 68, 52, 50, 224, 32, 95, + 228, 147, 1, 175, 134, 211, 65, 226, 13, 138, + 116, 222, 249, 77, 1, 10, 38, 3, 7, 52, + 160, 47, 96, 91, 0, 11, 83, 224, 128, 62, + 197, 176, 107, 32, 224, 222, 217, 192, 240, 130, + 25, 109, 16, 133, 91, 242, 159, 15, 63, 37, + 176, 63, 25, 64, 0, 68, 108, 128, 7, 144, + 232, 0, 37, 146, 81, 68, 14, 208, 20, 20, + 163, 72, 167, 216, 128, 14, 85, 201, 66, 205, + 155, 110, 224, 61, 37, 220, 255, 16, 63, 226, + 235, 215, 135, 194, 5, 44, 78, 249, 5, 61, + 86, 251, 65, 5, 36, 208, 23, 15, 52, 177, + 136, 101, 100, 97, 18, 195, 197, 171, 52, 218, + 205, 117, 51, 108, 163, 169, 132, 16, 171, 54, + 118, 174, 142, 69, 48, 75, 248, 224, 22, 162, + 45, 146, 171, 51, 8, 160, 221, 25, 17, 105, + 70, 82, 90, 237, 91, 131, 137, 164, 37, 137, + 80, 73, 238, 221, 11, 86, 93, 9, 95, 242, + 180, 101, 128, 46, 10, 236, 148, 126, 177, 6, + 5, 70, 153, 68, 69, 38, 18, 2, 255, 42, + 34, 104, 34, 160, 74, 106, 9, 99, 8, 245, + 51, 230, 147, 126, 55, 131, 26, 196, 178, 35, + 225, 25, 153, 52, 131, 82, 55, 240, 84, 19, + 107, 60, 234, 11, 0, 12, 16, 1, 16, 148, + 160, 110, 43, 123, 100, 56, 243, 22, 2, 145, + 128, 45, 101, 16, 152, 15, 118, 44, 54, 131, + 100, 218, 239, 73, 111, 58, 66, 13, 120, 0, + 151, 133, 60, 205, 116, 248, 20, 12, 5, 246, + 227, 52, 126, 106, 195, 120, 50, 56, 146, 1, + 46, 247, 1, 126, 86, 141, 156, 79, 132, 192, + 61, 67, 240, 1, 117, 182, 6, 85, 141, 90, + 38, 232, 134, 176, 17, 89, 198, 165, 35, 134, + 17, 70, 14, 92, 32, 63, 134, 141, 224, 117, + 176, 11, 105, 215, 44, 232, 208, 136, 178, 72, + 34, 118, 212, 203, 69, 173, 226, 148, 26, 216, + 133, 7, 255, 39, 80, 192, 4, 102, 42, 191, + 17, 136, 52, 164, 37, 109, 211, 253, 48, 137, + 49, 122, 90, 116, 165, 255, 100, 64, 86, 40, + 74, 128, 22, 0, 224, 6, 48, 152, 128, 2, + 64, 122, 211, 187, 229, 84, 153, 142, 226, 169, + 18, 230, 41, 128, 164, 1, 149, 31, 37, 98, + 166, 93, 134, 192, 129, 133, 49, 181, 169, 36, + 125, 234, 147, 36, 68, 71, 47, 80, 213, 170, + 252, 32, 79, 92, 232, 177, 4, 14, 80, 32, + 103, 77, 229, 218, 83, 177, 51, 214, 6, 73, + 53, 10, 103, 237, 71, 67, 126, 96, 0, 164, + 133, 69, 4, 128, 147, 134, 15, 72, 240, 85, + 174, 229, 236, 176, 175, 203, 105, 93, 223, 116, + 215, 41, 228, 53, 30, 43, 177, 65, 14, 94, + 80, 133, 21, 160, 64, 93, 75, 153, 0, 0, + 158, 224, 130, 19, 60, 224, 110, 57, 99, 216, + 237, 74, 0, 130, 8, 120, 192, 3, 99, 243, + 192, 97, 234, 58, 161, 120, 42, 226, 177, 106, + 9, 26, 37, 57, 167, 131, 23, 8, 207, 39, + 20, 248, 1, 254, 136, 80, 3, 20, 44, 21, + 180, 32, 32, 79, 4, 80, 16, 128, 11, 64, + 64, 4, 34, 80, 65, 7, 94, 80, 38, 214, + 202, 102, 183, 105, 168, 1, 12, 170, 26, 219, + 26, 34, 19, 135, 29, 41, 128, 117, 137, 128, + 3, 9, 252, 214, 176, 15, 24, 64, 15, 56, + 247, 12, 24, 168, 75, 4, 6, 0, 135, 192, + 14, 156, 139, 82, 90, 8, 164, 38, 219, 29, + 2, 79, 150, 194, 0, 24, 44, 97, 7, 22, + 184, 29, 195, 58, 106, 223, 58, 230, 160, 156, + 68, 89, 108, 89, 115, 145, 3, 147, 48, 33, + 131, 50, 89, 0, 3, 4, 16, 95, 33, 220, + 224, 5, 26, 64, 193, 91, 111, 167, 57, 35, + 132, 35, 53, 148, 116, 12, 100, 166, 248, 13, + 143, 241, 163, 59, 34, 16, 192, 15, 120, 208, + 222, 36, 208, 192, 49, 63, 216, 192, 9, 36, + 192, 128, 137, 114, 152, 44, 47, 217, 140, 130, + 9, 114, 25, 28, 76, 148, 9, 162, 2, 128, + 141, 219, 50, 5, 122, 134, 120, 196, 174, 141, + 46, 143, 167, 96, 131, 23, 221, 120, 200, 72, + 214, 74, 16, 0, 0, 59 }; + +static const unsigned char php_logo[] = { + 71, 73, 70, 56, 57, 97, 120, 0, 67, 0, + 230, 106, 0, 127, 130, 184, 57, 55, 71, 40, + 37, 42, 204, 205, 226, 161, 164, 203, 211, 213, + 231, 178, 180, 212, 67, 66, 88, 131, 134, 185, + 130, 131, 179, 82, 82, 114, 144, 146, 194, 194, + 196, 222, 170, 172, 208, 76, 75, 99, 91, 92, + 131, 221, 222, 236, 59, 56, 60, 110, 113, 165, + 106, 109, 157, 97, 99, 141, 117, 121, 177, 123, + 126, 181, 229, 230, 240, 153, 156, 198, 140, 141, + 193, 185, 186, 217, 107, 107, 146, 78, 78, 107, + 113, 116, 169, 122, 122, 163, 136, 139, 189, 114, + 116, 163, 116, 115, 152, 142, 144, 193, 90, 91, + 126, 226, 227, 239, 123, 125, 173, 164, 165, 208, + 109, 112, 162, 114, 118, 172, 149, 150, 200, 187, + 189, 217, 116, 120, 174, 133, 136, 187, 146, 149, + 195, 216, 217, 234, 146, 146, 196, 100, 102, 146, + 107, 110, 159, 165, 168, 206, 148, 150, 197, 46, + 43, 47, 83, 81, 104, 179, 180, 215, 108, 106, + 140, 92, 91, 118, 138, 141, 191, 102, 104, 150, + 104, 106, 154, 156, 159, 200, 49, 46, 57, 174, + 176, 211, 156, 156, 205, 85, 86, 120, 158, 161, + 202, 150, 153, 197, 129, 130, 175, 103, 105, 151, + 63, 61, 80, 188, 190, 218, 94, 96, 137, 152, + 153, 200, 140, 142, 191, 137, 138, 186, 87, 88, + 124, 182, 183, 215, 213, 215, 232, 34, 30, 32, + 108, 111, 158, 206, 208, 228, 191, 192, 220, 119, + 123, 180, 118, 120, 167, 95, 94, 125, 153, 153, + 204, 110, 111, 152, 115, 119, 174, 34, 30, 31, + 255, 255, 255, 144, 142, 143, 89, 86, 87, 199, + 198, 199, 238, 238, 245, 213, 212, 213, 246, 246, + 250, 130, 128, 129, 172, 170, 171, 116, 114, 115, + 241, 240, 241, 158, 156, 157, 227, 226, 227, 75, + 72, 73, 185, 184, 185, 103, 100, 101, 137, 137, + 182, 0, 255, 0, 71, 70, 95, 223, 224, 237, + 155, 156, 204, 105, 107, 156, 111, 115, 167, 140, + 140, 186, 184, 185, 217, 184, 186, 215, 154, 155, + 204, 167, 170, 207, 219, 220, 235, 154, 156, 201, + 102, 100, 132, 104, 103, 137, 167, 168, 210, 110, + 112, 160, 139, 139, 185, 198, 199, 224, 199, 201, + 225, 105, 108, 156, 151, 152, 203, 33, 249, 4, + 1, 0, 0, 106, 0, 44, 0, 0, 0, 0, + 120, 0, 67, 0, 0, 7, 255, 128, 106, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 150, 109, 63, 109, 115, 152, 158, 159, 160, + 63, 121, 121, 54, 62, 26, 113, 76, 26, 26, + 76, 6, 62, 62, 13, 50, 4, 65, 60, 24, + 66, 45, 11, 73, 34, 57, 31, 25, 57, 34, + 47, 41, 160, 194, 161, 13, 26, 12, 125, 77, + 5, 80, 80, 3, 125, 124, 12, 81, 81, 42, + 114, 172, 175, 116, 177, 179, 66, 51, 45, 186, + 31, 8, 0, 22, 22, 21, 87, 40, 37, 22, + 9, 25, 193, 195, 235, 140, 38, 113, 124, 46, + 108, 108, 16, 16, 117, 46, 201, 3, 212, 50, + 179, 34, 31, 254, 44, 0, 17, 8, 4, 64, + 176, 224, 55, 130, 225, 164, 172, 64, 241, 70, + 194, 9, 63, 59, 158, 76, 25, 146, 132, 157, + 69, 53, 65, 108, 12, 96, 67, 226, 130, 71, + 18, 36, 242, 53, 16, 178, 64, 138, 201, 147, + 40, 83, 170, 92, 121, 114, 92, 7, 9, 19, + 118, 232, 128, 65, 225, 8, 5, 43, 67, 68, + 92, 196, 20, 132, 9, 20, 142, 30, 47, 184, + 48, 66, 167, 5, 203, 163, 72, 147, 162, 188, + 242, 38, 6, 140, 35, 71, 30, 140, 88, 114, + 196, 131, 206, 157, 144, 126, 104, 116, 1, 129, + 35, 4, 35, 4, 88, 40, 29, 75, 54, 233, + 149, 19, 58, 30, 44, 1, 162, 64, 193, 136, + 41, 51, 255, 176, 46, 50, 161, 161, 64, 19, + 23, 117, 10, 24, 72, 82, 182, 175, 95, 164, + 29, 96, 44, 81, 192, 129, 131, 3, 60, 9, + 228, 26, 202, 19, 101, 0, 148, 2, 3, 124, + 228, 248, 187, 114, 1, 134, 203, 152, 49, 124, + 160, 156, 244, 13, 5, 5, 14, 214, 172, 81, + 224, 65, 177, 154, 60, 70, 248, 244, 25, 192, + 100, 6, 231, 149, 31, 178, 112, 153, 77, 155, + 75, 151, 215, 74, 221, 44, 89, 115, 224, 128, + 131, 16, 59, 221, 69, 97, 192, 160, 1, 2, + 220, 43, 49, 112, 193, 194, 188, 185, 153, 219, + 200, 147, 118, 216, 93, 164, 200, 154, 210, 195, + 126, 248, 144, 99, 68, 5, 15, 11, 209, 87, + 170, 32, 211, 188, 185, 152, 2, 225, 149, 94, + 121, 112, 32, 64, 0, 14, 112, 64, 153, 48, + 192, 68, 14, 1, 0, 233, 87, 22, 16, 83, + 158, 57, 25, 21, 249, 41, 245, 134, 2, 238, + 245, 112, 7, 38, 115, 208, 225, 131, 1, 50, + 28, 23, 160, 74, 93, 152, 209, 31, 22, 92, + 16, 240, 160, 82, 49, 172, 209, 67, 15, 7, + 236, 81, 201, 15, 50, 52, 208, 64, 73, 23, + 166, 4, 64, 22, 19, 98, 145, 197, 102, 37, + 38, 245, 64, 0, 27, 2, 39, 73, 27, 4, + 236, 227, 96, 139, 39, 41, 55, 161, 25, 95, + 224, 168, 148, 31, 69, 208, 64, 3, 21, 145, + 216, 17, 4, 1, 51, 128, 135, 212, 255, 7, + 153, 53, 217, 36, 139, 71, 169, 16, 198, 132, + 98, 92, 208, 36, 137, 44, 49, 233, 228, 150, + 248, 141, 133, 130, 3, 52, 8, 80, 195, 35, + 72, 96, 16, 4, 148, 71, 125, 240, 69, 22, + 108, 182, 233, 230, 155, 89, 116, 81, 64, 3, + 93, 162, 84, 0, 24, 19, 146, 1, 103, 155, + 23, 12, 96, 225, 73, 11, 236, 41, 40, 155, + 93, 12, 208, 128, 82, 64, 8, 32, 128, 3, + 141, 204, 32, 4, 6, 98, 41, 69, 192, 114, + 41, 86, 106, 6, 24, 92, 124, 1, 32, 74, + 93, 108, 81, 105, 138, 52, 108, 161, 69, 25, + 93, 144, 104, 192, 148, 159, 78, 184, 5, 24, + 94, 116, 113, 40, 82, 35, 8, 224, 4, 163, + 138, 108, 35, 196, 141, 73, 141, 151, 234, 167, + 91, 120, 1, 65, 151, 39, 238, 250, 41, 24, + 99, 96, 32, 197, 0, 120, 10, 171, 106, 25, + 3, 36, 53, 130, 19, 78, 224, 144, 72, 10, + 34, 180, 128, 102, 82, 16, 240, 167, 108, 138, + 97, 64, 96, 18, 6, 94, 108, 155, 34, 26, + 183, 93, 224, 169, 184, 229, 209, 224, 69, 179, + 72, 173, 1, 45, 118, 133, 252, 225, 203, 100, + 101, 125, 33, 33, 186, 253, 121, 1, 160, 148, + 248, 230, 219, 0, 138, 253, 54, 71, 67, 25, + 127, 30, 21, 128, 19, 2, 44, 96, 72, 63, + 244, 146, 21, 91, 192, 229, 109, 209, 227, 157, + 16, 51, 167, 197, 255, 5, 225, 86, 140, 133, + 24, 208, 29, 213, 129, 172, 99, 18, 146, 2, + 2, 31, 212, 57, 214, 164, 59, 194, 57, 198, + 25, 218, 78, 232, 5, 1, 157, 78, 200, 197, + 155, 99, 112, 129, 70, 138, 90, 100, 129, 106, + 196, 112, 150, 113, 198, 185, 19, 150, 129, 37, + 75, 10, 64, 155, 6, 33, 222, 224, 58, 150, + 174, 253, 137, 225, 45, 74, 11, 52, 208, 5, + 121, 19, 106, 193, 0, 192, 253, 173, 136, 18, + 6, 13, 124, 17, 65, 213, 89, 104, 81, 53, + 187, 128, 26, 240, 69, 178, 253, 133, 97, 128, + 82, 52, 204, 58, 200, 31, 0, 32, 160, 100, + 89, 217, 230, 185, 169, 137, 89, 124, 221, 116, + 22, 25, 151, 199, 227, 74, 16, 0, 221, 92, + 206, 130, 51, 119, 198, 218, 42, 125, 48, 6, + 206, 119, 31, 181, 4, 180, 10, 171, 33, 130, + 5, 38, 147, 101, 175, 204, 5, 167, 20, 184, + 170, 58, 83, 249, 116, 74, 49, 247, 119, 70, + 22, 52, 4, 109, 236, 74, 161, 151, 167, 69, + 227, 44, 117, 0, 237, 13, 130, 176, 144, 80, + 95, 15, 79, 56, 198, 181, 39, 109, 222, 31, + 26, 89, 160, 221, 220, 127, 42, 5, 27, 116, + 25, 19, 210, 144, 197, 81, 169, 15, 206, 58, + 75, 69, 184, 173, 70, 56, 115, 147, 133, 114, + 127, 17, 244, 200, 210, 229, 253, 17, 142, 185, + 74, 224, 22, 159, 197, 25, 170, 94, 112, 20, + 255, 214, 229, 1, 175, 20, 7, 78, 244, 32, + 72, 5, 148, 49, 93, 30, 26, 159, 167, 84, + 123, 218, 228, 55, 119, 187, 74, 252, 246, 183, + 69, 216, 99, 179, 132, 1, 241, 219, 83, 10, + 5, 160, 37, 136, 21, 80, 166, 110, 217, 91, + 158, 73, 12, 0, 190, 160, 245, 173, 57, 213, + 211, 143, 239, 152, 3, 134, 44, 20, 14, 11, + 135, 99, 9, 3, 168, 214, 159, 251, 9, 144, + 128, 41, 184, 2, 251, 252, 130, 189, 242, 84, + 136, 37, 201, 99, 78, 4, 58, 183, 187, 248, + 157, 36, 133, 88, 8, 3, 233, 76, 183, 18, + 0, 120, 109, 66, 228, 34, 203, 0, 157, 32, + 8, 20, 24, 176, 47, 194, 235, 32, 238, 142, + 69, 169, 236, 245, 174, 106, 203, 11, 98, 121, + 188, 0, 192, 116, 29, 79, 63, 28, 44, 79, + 24, 20, 184, 146, 29, 10, 162, 3, 40, 24, + 33, 89, 116, 68, 189, 39, 162, 228, 3, 5, + 240, 66, 233, 250, 67, 131, 49, 124, 225, 130, + 39, 76, 73, 247, 38, 244, 189, 240, 113, 15, + 2, 98, 156, 80, 245, 42, 119, 20, 43, 170, + 97, 33, 90, 92, 90, 20, 153, 67, 46, 21, + 248, 81, 5, 16, 232, 194, 24, 180, 48, 70, + 209, 13, 160, 126, 204, 241, 32, 74, 242, 199, + 51, 177, 101, 175, 11, 127, 4, 100, 23, 202, + 224, 72, 153, 81, 113, 37, 232, 83, 223, 243, + 58, 112, 133, 190, 236, 71, 255, 85, 90, 8, + 101, 40, 197, 112, 175, 20, 137, 225, 11, 4, + 120, 160, 10, 189, 104, 167, 9, 98, 161, 130, + 23, 20, 149, 40, 181, 64, 202, 97, 137, 175, + 44, 205, 163, 213, 7, 222, 208, 129, 190, 68, + 72, 99, 104, 248, 194, 2, 78, 133, 67, 23, + 154, 196, 92, 19, 146, 97, 33, 3, 118, 202, + 161, 73, 231, 117, 130, 72, 129, 4, 222, 240, + 67, 165, 40, 177, 95, 205, 60, 150, 43, 173, + 182, 18, 68, 98, 129, 137, 26, 3, 131, 48, + 251, 242, 56, 39, 68, 238, 121, 18, 232, 229, + 88, 214, 216, 47, 26, 156, 97, 156, 82, 64, + 102, 127, 184, 240, 170, 173, 169, 146, 57, 109, + 12, 88, 4, 206, 112, 1, 103, 178, 205, 121, + 130, 120, 193, 9, 36, 208, 73, 165, 48, 82, + 92, 102, 32, 195, 24, 6, 80, 167, 25, 118, + 208, 159, 196, 212, 31, 255, 240, 101, 6, 25, + 50, 128, 142, 73, 41, 154, 19, 142, 70, 136, + 33, 156, 224, 13, 99, 161, 152, 254, 102, 57, + 75, 50, 112, 97, 12, 133, 66, 83, 160, 72, + 26, 74, 86, 158, 68, 5, 92, 96, 233, 232, + 46, 40, 6, 150, 134, 210, 164, 40, 101, 192, + 16, 165, 3, 50, 67, 164, 224, 4, 39, 80, + 39, 82, 96, 200, 133, 11, 208, 131, 30, 5, + 248, 35, 1, 252, 105, 18, 64, 30, 245, 168, + 153, 3, 212, 0, 158, 74, 143, 67, 46, 147, + 57, 94, 255, 48, 234, 81, 147, 234, 71, 2, + 236, 116, 44, 7, 75, 216, 33, 146, 16, 131, + 19, 160, 0, 41, 215, 76, 36, 83, 125, 36, + 133, 5, 52, 177, 60, 89, 192, 40, 114, 220, + 229, 4, 120, 25, 34, 1, 101, 45, 104, 114, + 238, 185, 66, 182, 178, 164, 1, 13, 140, 88, + 199, 242, 83, 78, 105, 41, 162, 4, 121, 101, + 201, 65, 249, 104, 76, 191, 74, 129, 1, 149, + 108, 14, 24, 208, 19, 160, 114, 210, 106, 17, + 136, 53, 171, 4, 171, 198, 0, 199, 170, 68, + 119, 82, 188, 36, 101, 52, 122, 89, 70, 148, + 96, 2, 49, 232, 64, 30, 165, 64, 212, 122, + 122, 214, 36, 121, 115, 217, 233, 162, 211, 129, + 3, 64, 171, 180, 141, 72, 0, 106, 37, 160, + 197, 180, 98, 97, 12, 107, 245, 209, 2, 22, + 199, 70, 185, 250, 133, 8, 61, 128, 150, 97, + 35, 161, 132, 39, 76, 64, 179, 82, 248, 223, + 22, 166, 75, 93, 222, 189, 214, 158, 212, 165, + 46, 24, 6, 251, 26, 5, 200, 74, 0, 50, + 154, 196, 11, 64, 224, 135, 24, 240, 22, 0, + 5, 160, 42, 61, 16, 119, 93, 41, 160, 87, + 189, 16, 112, 45, 101, 136, 112, 48, 39, 68, + 128, 163, 150, 72, 128, 27, 80, 251, 134, 213, + 182, 215, 175, 39, 176, 173, 114, 65, 241, 130, + 41, 248, 1, 181, 170, 253, 175, 95, 81, 192, + 1, 89, 57, 161, 8, 248, 5, 133, 255, 18, + 244, 224, 134, 3, 247, 87, 193, 37, 58, 193, + 26, 28, 220, 131, 240, 178, 131, 5, 20, 62, + 176, 4, 206, 138, 225, 240, 28, 225, 0, 138, + 18, 64, 15, 96, 167, 24, 22, 128, 96, 7, + 110, 112, 67, 12, 168, 89, 98, 202, 248, 129, + 3, 1, 8, 147, 0, 2, 192, 98, 211, 8, + 34, 9, 37, 32, 194, 14, 96, 60, 1, 26, + 215, 56, 41, 19, 32, 80, 15, 132, 36, 38, + 187, 250, 120, 16, 41, 72, 128, 30, 116, 64, + 4, 33, 187, 33, 168, 254, 197, 240, 27, 142, + 128, 227, 13, 45, 185, 8, 120, 120, 193, 147, + 23, 241, 2, 41, 235, 224, 204, 103, 222, 193, + 71, 73, 124, 221, 29, 28, 65, 1, 237, 129, + 209, 134, 14, 112, 3, 15, 141, 249, 17, 47, + 64, 192, 20, 136, 0, 131, 62, 251, 153, 8, + 126, 248, 168, 80, 243, 51, 1, 34, 60, 0, + 52, 213, 113, 143, 162, 113, 16, 2, 59, 223, + 153, 18, 34, 24, 194, 158, 41, 64, 233, 74, + 83, 250, 8, 125, 174, 112, 160, 223, 192, 105, + 78, 75, 224, 211, 159, 6, 42, 80, 137, 112, + 105, 182, 136, 166, 55, 7, 168, 78, 17, 2, + 112, 0, 28, 108, 32, 49, 143, 22, 70, 10, + 62, 80, 2, 16, 88, 1, 42, 80, 121, 128, + 174, 119, 61, 149, 37, 172, 5, 8, 108, 105, + 11, 97, 10, 99, 152, 208, 156, 186, 55, 69, + 80, 0, 21, 110, 56, 48, 4, 49, 199, 90, + 49, 41, 80, 2, 2, 60, 224, 129, 13, 108, + 32, 45, 190, 6, 118, 176, 219, 66, 108, 195, + 112, 128, 10, 35, 24, 129, 21, 172, 80, 130, + 102, 63, 251, 220, 139, 16, 65, 26, 16, 112, + 21, 116, 187, 251, 221, 240, 142, 119, 188, 3, + 1, 0, 59, 0 }; + +static const unsigned char php_egg_logo[] = { + 71, 73, 70, 56, 57, 97, 120, 0, 67, 0, + 231, 255, 0, 18, 25, 33, 32, 30, 34, 28, + 33, 44, 15, 35, 71, 6, 37, 85, 37, 40, + 47, 34, 41, 53, 41, 40, 43, 9, 47, 109, + 30, 45, 68, 21, 48, 84, 51, 46, 55, 43, + 49, 59, 31, 59, 98, 15, 61, 128, 58, 55, + 69, 50, 57, 74, 0, 66, 144, 56, 58, 60, + 54, 59, 71, 32, 66, 113, 60, 65, 67, 63, + 65, 84, 63, 68, 79, 28, 79, 145, 15, 82, + 162, 75, 72, 98, 68, 78, 86, 74, 77, 88, + 50, 82, 122, 41, 85, 134, 76, 78, 108, 70, + 83, 101, 5, 94, 190, 0, 95, 197, 86, 80, + 101, 28, 92, 159, 80, 84, 96, 83, 83, 115, + 81, 87, 89, 22, 97, 183, 86, 88, 85, 0, + 102, 210, 8, 101, 204, 24, 100, 177, 85, 90, + 127, 35, 101, 169, 42, 100, 159, 88, 92, 103, + 0, 107, 216, 69, 95, 132, 0, 108, 210, 84, + 93, 112, 94, 90, 117, 18, 105, 201, 96, 92, + 105, 89, 96, 98, 94, 93, 135, 94, 94, 126, + 30, 106, 210, 7, 112, 222, 33, 108, 198, 16, + 114, 217, 27, 113, 198, 24, 113, 209, 59, 108, + 159, 96, 100, 138, 0, 120, 229, 44, 112, 180, + 49, 112, 171, 71, 108, 148, 99, 104, 115, 90, + 106, 125, 27, 115, 232, 100, 102, 147, 42, 115, + 192, 86, 110, 137, 9, 123, 239, 106, 108, 105, + 105, 105, 138, 33, 118, 228, 12, 125, 227, 48, + 117, 208, 16, 126, 222, 37, 120, 224, 20, 125, + 235, 35, 123, 213, 50, 121, 190, 43, 123, 206, + 40, 123, 220, 105, 110, 160, 59, 122, 182, 78, + 119, 158, 44, 122, 233, 3, 134, 250, 112, 112, + 151, 73, 123, 175, 113, 118, 114, 51, 128, 205, + 34, 129, 246, 11, 136, 245, 34, 130, 240, 113, + 117, 149, 111, 116, 166, 111, 119, 141, 48, 130, + 221, 38, 132, 235, 53, 130, 215, 97, 124, 146, + 116, 122, 124, 117, 121, 131, 54, 130, 229, 41, + 135, 232, 68, 130, 223, 46, 135, 246, 65, 134, + 202, 42, 137, 241, 75, 131, 205, 124, 121, 161, + 101, 125, 191, 86, 128, 210, 121, 122, 169, 105, + 128, 157, 121, 122, 180, 114, 124, 181, 94, 128, + 201, 81, 134, 185, 46, 138, 252, 76, 135, 195, + 34, 142, 252, 62, 137, 229, 63, 138, 217, 33, + 144, 247, 58, 139, 223, 56, 141, 246, 54, 143, + 234, 57, 143, 241, 102, 135, 193, 129, 130, 176, + 122, 136, 144, 46, 148, 252, 45, 149, 246, 118, + 133, 191, 65, 146, 231, 73, 146, 216, 129, 133, + 185, 135, 135, 156, 69, 148, 226, 44, 153, 255, + 94, 143, 216, 132, 136, 175, 88, 146, 207, 137, + 139, 136, 93, 146, 197, 95, 147, 188, 137, 139, + 150, 104, 146, 178, 122, 143, 172, 135, 139, 191, + 57, 156, 254, 67, 154, 245, 70, 154, 239, 134, + 141, 180, 117, 145, 201, 120, 146, 190, 79, 154, + 233, 140, 141, 188, 129, 144, 194, 142, 142, 176, + 137, 147, 156, 88, 155, 222, 82, 157, 230, 93, + 157, 218, 66, 162, 253, 100, 157, 210, 77, 161, + 247, 55, 168, 255, 81, 162, 241, 148, 148, 195, + 145, 149, 202, 127, 156, 204, 77, 166, 255, 142, + 155, 177, 141, 153, 200, 123, 160, 193, 73, 171, + 255, 93, 167, 240, 96, 167, 234, 152, 158, 160, + 101, 167, 228, 119, 164, 206, 155, 158, 171, 110, + 167, 219, 156, 155, 203, 89, 172, 252, 128, 164, + 219, 151, 163, 174, 81, 178, 255, 158, 162, 202, + 102, 175, 249, 93, 178, 251, 108, 175, 237, 103, + 177, 244, 166, 168, 165, 97, 182, 255, 87, 185, + 255, 149, 174, 216, 125, 181, 235, 110, 185, 252, + 117, 183, 254, 171, 172, 209, 166, 175, 197, 103, + 190, 255, 174, 176, 189, 140, 183, 221, 128, 184, + 248, 120, 187, 249, 179, 180, 184, 115, 193, 253, + 170, 183, 193, 174, 181, 215, 107, 197, 255, 135, + 191, 246, 151, 192, 219, 187, 183, 213, 128, 197, + 253, 121, 202, 255, 147, 198, 239, 188, 191, 219, + 148, 200, 252, 192, 193, 197, 134, 205, 254, 171, + 199, 236, 183, 199, 218, 163, 207, 251, 142, 213, + 255, 158, 211, 253, 195, 205, 217, 166, 211, 245, + 205, 204, 228, 153, 218, 255, 190, 210, 236, 186, + 212, 231, 177, 213, 248, 162, 224, 255, 188, 221, + 250, 214, 219, 221, 214, 219, 233, 174, 229, 254, + 198, 225, 247, 209, 226, 248, 186, 233, 251, 227, + 230, 239, 225, 241, 252, 253, 255, 252, 255, 255, + 255, 33, 249, 4, 1, 10, 0, 255, 0, 44, + 0, 0, 0, 0, 120, 0, 67, 0, 0, 8, + 254, 0, 255, 9, 28, 72, 176, 160, 193, 131, + 8, 19, 42, 92, 200, 176, 161, 195, 135, 16, + 35, 74, 156, 72, 177, 162, 197, 139, 22, 131, + 105, 12, 134, 177, 163, 199, 143, 193, 164, 73, + 227, 38, 141, 28, 56, 112, 228, 76, 146, 20, + 41, 205, 152, 75, 99, 183, 100, 201, 50, 101, + 202, 147, 77, 79, 166, 102, 205, 250, 200, 19, + 100, 73, 114, 239, 238, 221, 123, 71, 148, 104, + 202, 163, 224, 86, 138, 124, 25, 115, 166, 205, + 72, 145, 246, 72, 221, 195, 39, 207, 30, 69, + 158, 118, 246, 220, 202, 208, 24, 184, 160, 252, + 248, 9, 29, 59, 212, 100, 75, 99, 53, 159, + 66, 93, 203, 182, 109, 84, 169, 124, 248, 156, + 57, 163, 165, 174, 150, 60, 138, 76, 113, 221, + 251, 207, 24, 183, 119, 97, 3, 135, 125, 71, + 78, 90, 76, 62, 142, 70, 141, 18, 53, 109, + 170, 227, 199, 144, 35, 75, 157, 91, 87, 137, + 229, 28, 74, 190, 228, 229, 139, 209, 43, 96, + 193, 247, 10, 203, 114, 212, 167, 82, 47, 93, + 197, 138, 81, 163, 166, 173, 19, 47, 201, 176, + 99, 59, 166, 171, 36, 135, 237, 22, 45, 114, + 216, 209, 203, 25, 98, 176, 191, 66, 195, 134, + 54, 230, 233, 150, 179, 100, 197, 106, 181, 90, + 158, 171, 57, 179, 110, 231, 124, 185, 145, 77, + 157, 186, 150, 218, 45, 76, 104, 111, 145, 71, + 86, 239, 133, 198, 200, 254, 145, 189, 199, 205, + 212, 212, 115, 244, 232, 157, 107, 86, 172, 185, + 234, 92, 197, 154, 137, 11, 231, 11, 77, 245, + 251, 178, 207, 40, 201, 254, 225, 131, 134, 39, + 138, 124, 103, 80, 73, 68, 13, 37, 141, 39, + 144, 213, 115, 14, 60, 235, 116, 211, 12, 123, + 197, 136, 67, 141, 106, 226, 156, 51, 78, 16, + 92, 208, 194, 7, 126, 28, 70, 166, 159, 9, + 26, 132, 104, 130, 29, 2, 254, 67, 32, 81, + 224, 200, 18, 91, 56, 240, 208, 35, 78, 51, + 238, 49, 40, 223, 58, 240, 88, 99, 132, 31, + 207, 168, 131, 14, 55, 206, 56, 115, 203, 41, + 167, 172, 229, 73, 36, 158, 196, 133, 216, 83, + 29, 58, 166, 69, 11, 26, 88, 96, 129, 6, + 95, 112, 230, 213, 81, 210, 188, 21, 27, 59, + 208, 208, 179, 14, 123, 205, 81, 227, 96, 55, + 235, 208, 227, 14, 23, 126, 244, 162, 204, 49, + 200, 64, 3, 13, 51, 194, 232, 162, 139, 50, + 105, 106, 131, 13, 54, 225, 132, 227, 205, 157, + 207, 96, 243, 204, 48, 195, 244, 72, 28, 126, + 103, 48, 233, 164, 6, 36, 246, 20, 210, 81, + 198, 224, 87, 143, 48, 12, 78, 8, 75, 43, + 242, 21, 3, 38, 60, 227, 116, 224, 2, 22, + 135, 32, 162, 41, 33, 112, 208, 129, 72, 35, + 135, 8, 242, 72, 40, 163, 150, 82, 202, 42, + 186, 172, 178, 203, 46, 169, 170, 154, 76, 50, + 254, 123, 158, 194, 97, 14, 22, 60, 240, 192, + 7, 188, 117, 228, 23, 74, 198, 88, 41, 219, + 56, 189, 36, 19, 203, 57, 235, 80, 147, 11, + 44, 176, 20, 211, 96, 51, 240, 192, 195, 14, + 5, 36, 88, 129, 8, 33, 116, 200, 81, 198, + 181, 101, 208, 1, 199, 27, 220, 110, 59, 69, + 22, 130, 20, 82, 200, 35, 165, 76, 34, 110, + 33, 147, 64, 162, 174, 42, 192, 200, 122, 223, + 25, 38, 216, 186, 192, 19, 24, 133, 52, 82, + 149, 247, 161, 179, 203, 35, 136, 8, 243, 72, + 57, 244, 80, 179, 220, 39, 185, 192, 163, 77, + 55, 244, 236, 227, 14, 5, 17, 0, 65, 7, + 25, 101, 120, 81, 197, 196, 216, 82, 97, 49, + 21, 81, 68, 145, 69, 22, 105, 116, 236, 113, + 26, 161, 148, 59, 136, 24, 98, 64, 210, 203, + 40, 28, 106, 161, 193, 2, 11, 88, 144, 171, + 68, 246, 74, 163, 226, 125, 207, 156, 105, 8, + 25, 212, 54, 218, 202, 39, 4, 227, 115, 142, + 54, 233, 177, 211, 65, 4, 51, 84, 113, 109, + 21, 77, 52, 1, 69, 23, 19, 15, 1, 197, + 16, 84, 12, 17, 197, 182, 112, 8, 146, 198, + 20, 86, 147, 139, 204, 49, 169, 78, 50, 200, + 32, 144, 56, 210, 97, 14, 15, 176, 28, 37, + 204, 198, 180, 228, 107, 108, 158, 60, 19, 202, + 43, 159, 200, 225, 133, 28, 175, 224, 3, 15, + 50, 176, 80, 2, 75, 254, 51, 94, 34, 140, + 143, 208, 68, 119, 129, 45, 210, 93, 48, 13, + 197, 210, 60, 240, 0, 133, 15, 111, 104, 138, + 8, 28, 27, 79, 1, 199, 35, 163, 234, 18, + 139, 155, 233, 206, 177, 97, 202, 22, 28, 112, + 128, 14, 17, 5, 227, 210, 204, 213, 69, 178, + 11, 40, 160, 16, 194, 8, 33, 134, 52, 17, + 142, 122, 197, 236, 156, 11, 51, 185, 52, 3, + 244, 62, 128, 207, 16, 49, 182, 93, 36, 125, + 120, 23, 135, 67, 193, 131, 15, 112, 148, 147, + 143, 58, 216, 8, 3, 249, 198, 89, 192, 1, + 199, 33, 143, 196, 18, 203, 42, 144, 104, 222, + 33, 31, 26, 120, 62, 194, 67, 162, 255, 121, + 159, 35, 149, 104, 58, 135, 21, 100, 64, 92, + 69, 139, 198, 238, 156, 236, 222, 226, 208, 131, + 207, 56, 13, 56, 80, 116, 19, 215, 54, 81, + 197, 24, 73, 23, 126, 248, 16, 62, 248, 144, + 133, 62, 110, 112, 194, 50, 202, 113, 8, 231, + 101, 193, 7, 64, 152, 220, 184, 30, 129, 42, + 93, 168, 66, 108, 29, 50, 129, 231, 52, 208, + 16, 89, 220, 162, 87, 248, 137, 68, 29, 178, + 160, 6, 53, 188, 225, 5, 85, 176, 86, 21, + 232, 1, 15, 113, 228, 98, 57, 173, 104, 143, + 151, 224, 241, 190, 248, 249, 160, 126, 114, 136, + 161, 181, 202, 16, 53, 167, 13, 97, 120, 105, + 184, 7, 7, 12, 224, 4, 111, 192, 65, 114, + 254, 144, 3, 66, 26, 14, 145, 6, 43, 88, + 1, 11, 107, 128, 196, 42, 86, 1, 65, 14, + 181, 224, 0, 1, 160, 160, 66, 100, 114, 139, + 181, 197, 230, 22, 117, 152, 194, 20, 162, 160, + 134, 42, 144, 64, 10, 248, 163, 67, 122, 214, + 17, 187, 79, 232, 45, 23, 226, 16, 7, 62, + 240, 97, 13, 5, 16, 109, 12, 215, 98, 4, + 207, 64, 193, 51, 77, 53, 66, 83, 84, 240, + 65, 26, 218, 113, 1, 3, 180, 1, 27, 144, + 19, 196, 183, 128, 128, 196, 66, 132, 98, 13, + 63, 72, 228, 15, 176, 32, 134, 68, 52, 241, + 62, 45, 8, 64, 0, 106, 144, 144, 89, 152, + 66, 22, 8, 186, 207, 40, 214, 32, 8, 107, + 216, 194, 5, 67, 144, 218, 15, 134, 208, 5, + 68, 184, 15, 30, 205, 120, 148, 25, 97, 1, + 157, 125, 208, 227, 25, 3, 136, 64, 12, 232, + 215, 136, 121, 200, 35, 31, 249, 168, 71, 61, + 242, 33, 143, 118, 108, 67, 30, 208, 128, 195, + 32, 218, 49, 1, 1, 180, 1, 121, 199, 120, + 197, 35, 136, 184, 134, 102, 146, 11, 137, 88, + 80, 228, 37, 104, 145, 36, 13, 72, 178, 80, + 5, 153, 5, 78, 50, 73, 157, 72, 92, 98, + 13, 135, 208, 135, 63, 252, 129, 132, 40, 116, + 193, 105, 192, 11, 5, 62, 92, 217, 140, 157, + 49, 162, 21, 104, 60, 199, 62, 224, 225, 10, + 2, 68, 128, 10, 114, 254, 32, 196, 49, 178, + 113, 129, 11, 112, 224, 159, 37, 224, 192, 6, + 46, 176, 129, 108, 148, 99, 21, 213, 40, 230, + 1, 78, 128, 3, 55, 240, 34, 29, 216, 8, + 197, 36, 122, 81, 10, 114, 61, 2, 93, 107, + 136, 230, 28, 230, 96, 13, 43, 202, 230, 1, + 1, 56, 128, 119, 10, 146, 150, 251, 240, 65, + 21, 88, 200, 194, 60, 198, 233, 143, 84, 164, + 193, 104, 72, 43, 195, 49, 214, 8, 143, 216, + 49, 226, 157, 197, 208, 70, 251, 216, 193, 5, + 2, 100, 160, 10, 129, 64, 4, 54, 54, 81, + 2, 24, 148, 128, 6, 48, 160, 193, 81, 3, + 202, 0, 115, 120, 99, 19, 19, 40, 128, 1, + 74, 208, 79, 6, 20, 32, 12, 222, 8, 133, + 45, 136, 241, 141, 103, 232, 194, 84, 143, 72, + 195, 15, 196, 112, 9, 87, 248, 226, 22, 28, + 58, 3, 20, 183, 71, 144, 89, 16, 201, 163, + 145, 185, 68, 74, 7, 145, 130, 106, 140, 83, + 15, 136, 40, 131, 181, 154, 48, 6, 111, 140, + 145, 61, 173, 72, 214, 131, 210, 83, 41, 2, + 160, 128, 12, 140, 104, 68, 57, 220, 80, 212, + 106, 224, 50, 151, 250, 248, 69, 9, 24, 208, + 6, 126, 28, 225, 2, 1, 32, 70, 53, 126, + 193, 88, 1, 0, 96, 17, 165, 96, 2, 65, + 113, 16, 6, 76, 96, 98, 27, 202, 16, 43, + 89, 51, 193, 10, 82, 108, 174, 58, 38, 144, + 254, 228, 203, 134, 4, 87, 200, 220, 1, 11, + 64, 160, 66, 16, 12, 80, 128, 118, 216, 195, + 5, 141, 40, 31, 182, 216, 145, 30, 23, 181, + 135, 149, 212, 8, 19, 60, 188, 145, 0, 4, + 100, 225, 19, 59, 155, 7, 13, 78, 80, 130, + 109, 60, 67, 24, 141, 128, 67, 33, 204, 81, + 130, 2, 84, 224, 30, 37, 152, 128, 4, 242, + 113, 167, 121, 216, 162, 152, 39, 120, 6, 8, + 4, 192, 128, 13, 112, 160, 159, 6, 192, 68, + 41, 122, 240, 131, 65, 0, 2, 16, 115, 48, + 171, 51, 238, 3, 69, 41, 254, 195, 173, 181, + 125, 12, 31, 122, 0, 132, 25, 248, 192, 3, + 211, 56, 0, 13, 54, 145, 5, 50, 236, 149, + 126, 236, 104, 17, 60, 186, 113, 44, 73, 73, + 120, 29, 174, 136, 37, 28, 102, 215, 10, 121, + 208, 192, 13, 71, 72, 135, 50, 38, 22, 133, + 66, 164, 163, 187, 12, 216, 6, 7, 24, 112, + 132, 114, 192, 33, 13, 144, 248, 69, 1, 0, + 112, 2, 98, 126, 54, 30, 211, 32, 6, 26, + 120, 11, 12, 32, 244, 64, 12, 115, 240, 195, + 18, 174, 0, 8, 87, 144, 226, 20, 164, 147, + 76, 36, 3, 48, 82, 243, 4, 120, 42, 3, + 182, 193, 10, 86, 48, 3, 18, 20, 32, 0, + 2, 32, 129, 26, 200, 80, 133, 194, 53, 129, + 12, 236, 160, 17, 60, 210, 7, 11, 113, 172, + 227, 204, 225, 144, 129, 254, 97, 65, 33, 12, + 129, 165, 131, 3, 55, 112, 131, 45, 142, 81, + 5, 42, 188, 225, 17, 237, 88, 241, 2, 36, + 107, 128, 69, 196, 66, 13, 130, 120, 6, 26, + 60, 187, 136, 106, 120, 22, 23, 186, 224, 90, + 60, 12, 0, 0, 78, 76, 161, 7, 88, 152, + 67, 16, 92, 176, 4, 34, 92, 97, 11, 91, + 200, 132, 53, 204, 35, 153, 51, 72, 146, 94, + 255, 120, 242, 84, 60, 177, 4, 41, 171, 64, + 5, 59, 0, 2, 5, 18, 128, 1, 56, 220, + 236, 90, 189, 83, 3, 59, 206, 97, 230, 102, + 64, 55, 23, 201, 61, 179, 55, 26, 128, 128, + 52, 188, 162, 24, 204, 128, 196, 52, 56, 0, + 131, 77, 88, 67, 24, 132, 64, 196, 33, 212, + 241, 139, 11, 20, 224, 8, 104, 184, 128, 0, + 170, 129, 8, 53, 100, 193, 25, 16, 0, 0, + 0, 182, 49, 104, 6, 36, 67, 153, 143, 184, + 68, 2, 0, 192, 133, 52, 160, 0, 11, 87, + 120, 129, 11, 88, 192, 110, 18, 144, 128, 8, + 153, 118, 70, 162, 34, 99, 129, 40, 10, 196, + 164, 128, 232, 193, 10, 98, 16, 131, 29, 248, + 27, 8, 133, 224, 32, 33, 132, 123, 173, 70, + 172, 35, 141, 235, 64, 6, 116, 97, 65, 172, + 51, 103, 98, 0, 25, 120, 133, 48, 218, 163, + 138, 84, 252, 51, 27, 232, 160, 6, 51, 160, + 193, 142, 108, 28, 97, 2, 12, 176, 7, 18, + 254, 250, 136, 142, 14, 102, 161, 19, 140, 102, + 64, 59, 178, 189, 1, 109, 188, 34, 20, 133, + 240, 131, 103, 57, 113, 136, 31, 44, 97, 14, + 91, 32, 1, 187, 89, 224, 110, 23, 144, 160, + 8, 96, 240, 3, 46, 146, 44, 149, 15, 4, + 96, 1, 247, 174, 206, 41, 176, 96, 131, 29, + 240, 32, 6, 42, 224, 247, 14, 124, 16, 5, + 58, 88, 221, 193, 85, 240, 2, 51, 194, 113, + 142, 110, 72, 40, 23, 202, 233, 70, 179, 202, + 209, 1, 2, 212, 129, 25, 108, 202, 133, 55, + 208, 192, 129, 18, 152, 227, 30, 230, 216, 6, + 49, 156, 64, 108, 8, 164, 226, 30, 48, 152, + 192, 6, 158, 65, 7, 53, 88, 1, 9, 158, + 61, 194, 52, 102, 76, 12, 109, 8, 35, 22, + 144, 232, 196, 161, 225, 32, 134, 43, 92, 161, + 8, 47, 120, 1, 17, 212, 237, 110, 12, 252, + 28, 211, 156, 128, 140, 18, 36, 41, 144, 215, + 194, 198, 19, 115, 0, 194, 10, 84, 192, 131, + 33, 240, 187, 223, 62, 160, 66, 178, 7, 14, + 49, 50, 208, 233, 96, 231, 152, 16, 123, 230, + 83, 142, 12, 59, 32, 22, 26, 103, 70, 43, + 230, 1, 130, 182, 95, 128, 6, 19, 184, 64, + 240, 139, 234, 134, 120, 84, 99, 197, 109, 240, + 134, 26, 232, 80, 7, 11, 104, 123, 27, 72, + 0, 192, 6, 228, 49, 14, 61, 165, 131, 6, + 0, 144, 0, 43, 4, 254, 49, 136, 43, 16, + 33, 8, 30, 136, 60, 188, 225, 237, 238, 23, + 20, 97, 11, 153, 127, 204, 230, 3, 240, 223, + 234, 140, 2, 11, 83, 142, 58, 15, 202, 80, + 133, 167, 199, 96, 8, 141, 128, 155, 33, 62, + 65, 6, 67, 112, 29, 26, 21, 114, 14, 204, + 144, 83, 212, 112, 14, 222, 80, 118, 98, 128, + 12, 180, 35, 12, 185, 32, 15, 16, 0, 103, + 71, 0, 98, 71, 0, 3, 48, 224, 6, 219, + 48, 15, 143, 32, 9, 28, 48, 1, 188, 80, + 14, 84, 160, 6, 137, 192, 104, 23, 144, 14, + 217, 38, 1, 188, 112, 130, 188, 0, 3, 152, + 149, 10, 230, 50, 8, 150, 22, 4, 50, 0, + 6, 91, 64, 4, 46, 224, 2, 145, 7, 126, + 65, 16, 4, 91, 128, 11, 234, 199, 121, 255, + 16, 23, 178, 81, 7, 82, 32, 2, 168, 22, + 3, 62, 48, 4, 100, 160, 6, 79, 23, 5, + 135, 240, 10, 173, 48, 45, 251, 7, 13, 231, + 160, 38, 102, 118, 14, 117, 82, 39, 126, 0, + 113, 186, 128, 12, 12, 88, 11, 185, 176, 13, + 19, 112, 3, 37, 192, 15, 185, 84, 15, 243, + 16, 15, 234, 112, 12, 136, 80, 10, 237, 64, + 12, 196, 112, 15, 215, 64, 5, 112, 128, 11, + 22, 208, 103, 182, 32, 1, 0, 192, 0, 239, + 5, 80, 23, 208, 6, 207, 80, 8, 96, 3, + 111, 70, 128, 6, 118, 192, 4, 96, 0, 121, + 254, 36, 240, 2, 96, 176, 33, 150, 192, 5, + 92, 224, 43, 235, 39, 16, 103, 224, 121, 144, + 49, 10, 63, 48, 3, 163, 23, 3, 195, 243, + 129, 101, 160, 6, 169, 247, 10, 200, 0, 10, + 204, 209, 10, 194, 16, 14, 201, 195, 34, 7, + 151, 12, 225, 0, 12, 10, 224, 0, 147, 160, + 13, 180, 227, 133, 208, 240, 11, 196, 230, 6, + 253, 112, 12, 159, 208, 119, 86, 103, 8, 214, + 246, 10, 209, 80, 14, 209, 0, 10, 67, 0, + 7, 227, 16, 15, 247, 192, 15, 72, 192, 91, + 246, 176, 13, 169, 176, 8, 110, 144, 10, 214, + 5, 61, 35, 227, 2, 91, 32, 3, 86, 33, + 21, 92, 0, 6, 47, 224, 1, 50, 32, 10, + 176, 17, 137, 63, 72, 137, 142, 113, 10, 98, + 208, 116, 81, 199, 111, 60, 64, 5, 143, 83, + 6, 67, 160, 6, 177, 16, 14, 208, 160, 13, + 106, 194, 12, 200, 160, 12, 218, 112, 12, 82, + 152, 12, 187, 160, 10, 151, 64, 1, 8, 240, + 3, 10, 184, 51, 148, 208, 10, 139, 85, 2, + 37, 176, 9, 253, 32, 12, 134, 80, 6, 251, + 83, 62, 84, 144, 45, 141, 64, 8, 84, 192, + 3, 158, 8, 4, 86, 64, 10, 28, 32, 0, + 56, 224, 13, 194, 176, 11, 222, 80, 14, 202, + 16, 10, 135, 32, 46, 141, 7, 111, 45, 224, + 24, 145, 144, 9, 91, 0, 6, 70, 0, 6, + 112, 101, 116, 72, 247, 254, 15, 123, 112, 6, + 176, 65, 11, 63, 176, 111, 59, 208, 111, 154, + 216, 142, 135, 0, 10, 136, 16, 10, 194, 160, + 13, 201, 160, 13, 117, 34, 14, 208, 128, 12, + 186, 208, 11, 187, 112, 9, 128, 224, 1, 20, + 64, 0, 8, 96, 3, 203, 1, 11, 159, 112, + 83, 175, 80, 15, 52, 208, 118, 196, 144, 15, + 185, 32, 66, 67, 80, 5, 100, 64, 7, 81, + 83, 56, 85, 48, 3, 20, 233, 3, 54, 208, + 3, 157, 192, 0, 0, 64, 12, 208, 0, 10, + 63, 4, 7, 202, 54, 146, 133, 48, 7, 151, + 64, 4, 96, 112, 146, 144, 225, 7, 96, 192, + 5, 226, 40, 21, 245, 38, 69, 158, 48, 23, + 145, 97, 10, 115, 96, 3, 34, 48, 122, 231, + 88, 122, 112, 160, 6, 165, 16, 11, 161, 112, + 12, 202, 112, 9, 151, 16, 14, 226, 32, 143, + 244, 40, 6, 24, 224, 0, 8, 128, 0, 17, + 128, 2, 135, 128, 66, 173, 64, 9, 140, 64, + 9, 185, 208, 15, 196, 144, 10, 191, 192, 15, + 236, 0, 10, 114, 96, 56, 93, 32, 7, 73, + 120, 67, 80, 64, 5, 59, 128, 132, 84, 0, + 4, 107, 48, 104, 5, 240, 13, 194, 0, 10, + 111, 0, 4, 64, 32, 57, 105, 128, 46, 171, + 48, 7, 223, 39, 4, 145, 1, 132, 157, 246, + 105, 2, 49, 11, 115, 33, 142, 167, 240, 3, + 54, 224, 3, 153, 152, 56, 80, 243, 6, 112, + 254, 0, 138, 177, 32, 12, 208, 160, 10, 106, + 130, 13, 0, 24, 14, 197, 128, 148, 107, 208, + 3, 86, 32, 138, 202, 193, 51, 85, 121, 83, + 173, 112, 14, 229, 192, 14, 234, 80, 14, 173, + 160, 87, 101, 0, 60, 33, 148, 132, 112, 32, + 60, 60, 48, 3, 184, 233, 3, 170, 224, 75, + 237, 128, 13, 79, 72, 5, 84, 246, 45, 83, + 0, 136, 221, 71, 4, 50, 64, 147, 29, 178, + 100, 35, 37, 147, 132, 249, 24, 137, 128, 152, + 43, 224, 111, 57, 121, 132, 92, 4, 7, 161, + 160, 12, 111, 162, 11, 115, 32, 153, 199, 176, + 117, 63, 83, 158, 244, 8, 10, 148, 48, 144, + 237, 25, 8, 44, 26, 8, 114, 16, 8, 169, + 193, 8, 11, 41, 56, 133, 195, 3, 244, 99, + 117, 84, 80, 5, 80, 192, 111, 244, 195, 63, + 64, 16, 10, 186, 32, 12, 5, 234, 3, 49, + 48, 3, 195, 153, 6, 88, 48, 50, 240, 246, + 1, 73, 178, 7, 253, 213, 86, 90, 224, 160, + 142, 33, 6, 33, 48, 101, 254, 118, 67, 169, + 39, 8, 175, 208, 132, 210, 179, 11, 189, 176, + 5, 202, 160, 12, 107, 194, 117, 83, 216, 10, + 180, 83, 149, 44, 234, 5, 99, 224, 5, 127, + 224, 5, 110, 58, 6, 178, 89, 6, 74, 51, + 49, 80, 192, 144, 246, 83, 6, 159, 210, 119, + 166, 55, 75, 12, 57, 3, 213, 201, 132, 160, + 160, 6, 81, 48, 4, 254, 51, 48, 3, 89, + 96, 68, 73, 234, 125, 65, 96, 2, 73, 18, + 91, 1, 240, 50, 255, 160, 8, 81, 234, 24, + 142, 176, 4, 33, 32, 2, 137, 57, 3, 84, + 0, 5, 29, 84, 53, 64, 154, 42, 170, 144, + 9, 92, 112, 9, 165, 16, 143, 196, 98, 133, + 12, 184, 51, 104, 170, 166, 99, 176, 166, 115, + 227, 162, 173, 154, 52, 247, 217, 101, 89, 87, + 6, 132, 208, 8, 62, 89, 5, 67, 144, 147, + 19, 195, 3, 43, 96, 3, 81, 240, 10, 141, + 160, 6, 95, 217, 159, 126, 106, 3, 71, 36, + 6, 68, 80, 4, 50, 144, 36, 106, 21, 0, + 108, 5, 165, 147, 42, 21, 166, 96, 169, 83, + 214, 111, 137, 83, 5, 206, 3, 10, 161, 48, + 61, 170, 224, 7, 32, 0, 1, 50, 0, 9, + 186, 64, 39, 86, 104, 120, 202, 161, 170, 140, + 224, 166, 106, 250, 7, 109, 234, 166, 49, 228, + 5, 134, 224, 5, 73, 67, 63, 114, 80, 5, + 183, 154, 58, 55, 164, 2, 177, 73, 6, 80, + 96, 164, 108, 214, 10, 92, 198, 63, 51, 16, + 2, 6, 75, 95, 138, 42, 165, 248, 1, 82, + 34, 117, 16, 166, 80, 23, 155, 115, 11, 87, + 96, 161, 51, 192, 111, 156, 170, 108, 160, 32, + 61, 171, 192, 10, 32, 16, 124, 48, 208, 9, + 189, 128, 148, 208, 16, 14, 176, 72, 59, 55, + 69, 154, 129, 192, 166, 127, 176, 166, 114, 163, + 254, 166, 94, 0, 49, 77, 192, 3, 73, 208, + 4, 115, 67, 6, 160, 128, 171, 132, 160, 6, + 60, 10, 49, 60, 96, 3, 112, 112, 179, 100, + 16, 8, 236, 8, 4, 54, 96, 176, 33, 128, + 2, 40, 192, 2, 68, 160, 4, 213, 116, 77, + 9, 33, 169, 90, 48, 21, 115, 160, 111, 153, + 184, 3, 114, 136, 58, 135, 208, 173, 171, 224, + 10, 253, 132, 6, 81, 113, 10, 172, 240, 38, + 216, 160, 13, 242, 40, 164, 104, 250, 7, 129, + 224, 174, 129, 192, 8, 114, 208, 166, 134, 128, + 63, 17, 147, 56, 77, 32, 155, 132, 32, 138, + 136, 80, 5, 58, 11, 5, 47, 219, 5, 253, + 9, 7, 46, 74, 8, 236, 232, 3, 244, 133, + 2, 71, 139, 180, 44, 16, 4, 73, 178, 100, + 148, 164, 16, 121, 80, 23, 82, 225, 10, 55, + 153, 137, 70, 168, 6, 136, 32, 8, 47, 86, + 8, 165, 160, 9, 19, 96, 6, 142, 193, 7, + 163, 176, 11, 202, 192, 117, 242, 232, 78, 108, + 235, 174, 238, 58, 6, 104, 203, 170, 73, 227, + 5, 73, 48, 179, 65, 139, 171, 175, 80, 109, + 84, 80, 52, 178, 217, 4, 187, 58, 4, 217, + 82, 5, 83, 99, 5, 61, 128, 180, 190, 203, + 2, 46, 192, 180, 78, 36, 73, 254, 197, 184, + 117, 145, 7, 180, 192, 116, 138, 201, 111, 133, + 74, 117, 89, 112, 8, 186, 160, 9, 23, 16, + 25, 159, 171, 83, 240, 254, 120, 66, 104, 58, + 6, 129, 176, 166, 174, 10, 167, 100, 80, 175, + 245, 138, 51, 160, 144, 11, 112, 153, 71, 47, + 84, 62, 49, 91, 122, 116, 0, 60, 81, 0, + 4, 152, 122, 180, 75, 144, 1, 25, 112, 184, + 17, 68, 188, 15, 209, 184, 90, 80, 173, 54, + 80, 168, 167, 39, 60, 22, 243, 6, 208, 43, + 189, 146, 49, 10, 218, 112, 174, 19, 119, 44, + 60, 67, 186, 45, 234, 162, 114, 80, 175, 93, + 86, 6, 159, 0, 10, 177, 11, 7, 81, 112, + 132, 47, 75, 175, 78, 99, 52, 60, 160, 2, + 86, 90, 168, 54, 176, 4, 46, 144, 1, 87, + 112, 141, 213, 113, 6, 245, 102, 111, 16, 33, + 169, 138, 48, 177, 152, 184, 147, 19, 153, 122, + 28, 154, 12, 156, 48, 189, 146, 241, 12, 87, + 8, 13, 26, 215, 30, 237, 89, 11, 200, 128, + 12, 178, 3, 93, 235, 234, 5, 101, 144, 132, + 208, 149, 41, 21, 252, 144, 220, 27, 60, 117, + 250, 116, 161, 228, 163, 132, 116, 5, 46, 144, + 8, 248, 161, 4, 11, 32, 73, 139, 27, 17, + 166, 240, 5, 144, 208, 3, 251, 187, 111, 165, + 39, 135, 136, 240, 10, 146, 41, 192, 146, 113, + 10, 231, 96, 133, 212, 32, 12, 112, 195, 162, + 104, 27, 8, 173, 0, 55, 183, 214, 10, 55, + 85, 149, 79, 200, 173, 144, 99, 96, 170, 35, + 92, 192, 51, 4, 116, 64, 127, 161, 244, 129, + 254, 83, 80, 156, 147, 16, 100, 222, 8, 91, + 80, 116, 0, 103, 51, 17, 193, 192, 10, 145, + 123, 161, 6, 118, 181, 175, 240, 38, 153, 64, + 195, 53, 76, 178, 218, 128, 12, 185, 80, 149, + 109, 251, 7, 132, 32, 7, 134, 240, 7, 49, + 180, 182, 107, 11, 187, 91, 122, 8, 82, 96, + 3, 89, 128, 8, 173, 208, 8, 154, 92, 6, + 141, 208, 8, 129, 96, 8, 3, 103, 117, 207, + 35, 81, 27, 203, 131, 212, 161, 4, 32, 21, + 0, 18, 0, 169, 18, 49, 14, 107, 128, 137, + 251, 150, 122, 87, 43, 12, 245, 8, 12, 27, + 240, 151, 82, 241, 12, 218, 64, 13, 56, 44, + 12, 181, 192, 8, 105, 123, 83, 210, 44, 55, + 160, 204, 162, 148, 64, 48, 185, 16, 10, 161, + 80, 68, 105, 48, 190, 114, 76, 205, 55, 149, + 79, 172, 71, 45, 112, 208, 8, 163, 178, 10, + 201, 128, 203, 176, 161, 5, 39, 60, 73, 30, + 49, 8, 82, 230, 197, 95, 169, 202, 109, 162, + 12, 192, 64, 3, 150, 0, 27, 201, 35, 12, + 108, 34, 12, 210, 220, 162, 55, 229, 162, 110, + 26, 203, 238, 249, 198, 177, 160, 12, 144, 48, + 8, 177, 112, 12, 194, 0, 148, 205, 1, 55, + 210, 44, 205, 229, 67, 7, 201, 6, 42, 12, + 148, 12, 190, 0, 27, 124, 240, 1, 80, 20, + 0, 46, 243, 17, 170, 96, 5, 133, 122, 155, + 31, 168, 202, 14, 141, 254, 12, 189, 128, 4, + 146, 32, 25, 124, 160, 13, 171, 44, 144, 45, + 106, 8, 134, 240, 162, 11, 188, 182, 208, 149, + 66, 229, 169, 13, 151, 160, 10, 247, 88, 10, + 68, 89, 182, 85, 233, 96, 194, 101, 209, 81, + 160, 108, 151, 211, 11, 192, 240, 24, 168, 192, + 7, 42, 243, 209, 11, 144, 200, 30, 145, 12, + 105, 80, 168, 205, 171, 6, 231, 252, 10, 151, + 179, 11, 156, 112, 4, 252, 188, 198, 100, 112, + 205, 46, 26, 208, 114, 243, 162, 238, 233, 133, + 104, 87, 143, 47, 192, 10, 202, 80, 10, 200, + 64, 148, 225, 160, 198, 104, 26, 8, 179, 137, + 183, 114, 120, 8, 166, 162, 212, 190, 66, 43, + 158, 115, 0, 243, 194, 21, 127, 232, 167, 51, + 224, 111, 84, 87, 203, 165, 176, 11, 154, 0, + 3, 220, 244, 24, 189, 16, 206, 14, 214, 182, + 11, 108, 8, 105, 107, 211, 239, 36, 12, 150, + 29, 39, 30, 176, 5, 172, 208, 11, 14, 205, + 12, 218, 80, 11, 235, 73, 9, 44, 42, 155, + 134, 128, 49, 181, 60, 9, 170, 48, 10, 230, + 161, 5, 31, 240, 0, 125, 253, 0, 160, 182, + 23, 147, 64, 180, 23, 58, 117, 84, 23, 5, + 130, 112, 42, 174, 0, 3, 150, 240, 151, 171, + 144, 58, 1, 205, 174, 114, 80, 205, 160, 108, + 214, 3, 25, 209, 185, 160, 13, 202, 208, 0, + 3, 224, 1, 151, 32, 166, 152, 57, 48, 134, + 254, 112, 83, 16, 115, 45, 114, 184, 6, 125, + 112, 7, 123, 160, 5, 241, 178, 0, 125, 61, + 2, 216, 196, 23, 144, 80, 96, 21, 91, 177, + 253, 19, 5, 83, 112, 216, 192, 80, 2, 95, + 96, 23, 82, 234, 9, 165, 128, 8, 65, 27, + 180, 110, 74, 8, 163, 221, 182, 54, 157, 205, + 181, 192, 128, 185, 240, 10, 208, 160, 11, 13, + 160, 0, 24, 0, 6, 208, 32, 158, 208, 208, + 30, 234, 42, 223, 85, 144, 5, 216, 45, 23, + 57, 208, 218, 44, 227, 221, 22, 240, 4, 90, + 241, 29, 170, 64, 157, 215, 170, 147, 196, 147, + 208, 201, 128, 4, 102, 96, 25, 32, 94, 23, + 147, 120, 7, 125, 176, 6, 113, 160, 6, 214, + 60, 55, 106, 122, 223, 114, 4, 79, 39, 148, + 11, 255, 77, 1, 20, 112, 5, 170, 48, 182, + 205, 12, 205, 159, 64, 57, 120, 144, 221, 219, + 157, 3, 38, 80, 43, 101, 195, 50, 19, 238, + 203, 189, 1, 185, 243, 188, 188, 62, 48, 5, + 202, 128, 13, 201, 192, 6, 110, 224, 86, 121, + 0, 226, 82, 174, 4, 34, 126, 6, 36, 222, + 7, 120, 96, 226, 220, 226, 56, 17, 44, 154, + 151, 227, 148, 118, 201, 10, 124, 50, 10, 142, + 176, 33, 215, 225, 227, 77, 2, 228, 182, 242, + 0, 53, 240, 5, 68, 94, 34, 229, 120, 169, + 137, 169, 2, 6, 150, 5, 229, 186, 206, 48, + 64, 16, 166, 160, 8, 192, 81, 62, 229, 32, + 142, 25, 84, 238, 222, 115, 49, 232, 132, 110, + 23, 215, 113, 25, 218, 17, 34, 78, 178, 232, + 181, 98, 1, 109, 30, 32, 37, 162, 16, 151, + 64, 184, 152, 26, 127, 73, 94, 10, 73, 9, + 12, 56, 128, 16, 218, 148, 7, 121, 240, 5, + 182, 17, 234, 162, 158, 3, 184, 129, 27, 218, + 113, 234, 218, 209, 31, 253, 17, 34, 172, 206, + 232, 38, 160, 3, 0, 82, 225, 145, 206, 16, + 148, 142, 169, 116, 126, 164, 186, 144, 12, 187, + 237, 16, 150, 20, 9, 118, 96, 7, 95, 240, + 5, 216, 145, 29, 168, 158, 234, 170, 174, 1, + 31, 160, 3, 184, 17, 236, 120, 33, 235, 179, + 254, 16, 144, 75, 184, 136, 153, 106, 22, 185, + 6, 147, 192, 10, 174, 176, 1, 60, 65, 19, + 145, 240, 230, 207, 222, 17, 128, 64, 184, 33, + 96, 164, 86, 144, 6, 225, 226, 165, 154, 32, + 1, 223, 190, 238, 21, 225, 10, 75, 128, 2, + 54, 240, 3, 66, 148, 6, 228, 178, 11, 192, + 192, 4, 236, 158, 239, 2, 17, 16, 0, 59}; + diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..be289c8 --- /dev/null +++ b/main/main.c @@ -0,0 +1,2645 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* {{{ includes + */ + +#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS + +#include "php.h" +#include +#include +#ifdef PHP_WIN32 +#include "win32/time.h" +#include "win32/signal.h" +#include "win32/php_win32_globals.h" +#include "win32/winutil.h" +#include +#elif defined(NETWARE) +#include +#ifdef USE_WINSOCK +#include +#endif +#endif +#if HAVE_SYS_TIME_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_SIGNAL_H +#include +#endif +#if HAVE_SETLOCALE +#include +#endif +#include "zend.h" +#include "zend_extensions.h" +#include "php_ini.h" +#include "php_globals.h" +#include "php_main.h" +#include "fopen_wrappers.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/php_string.h" +#include "ext/date/php_date.h" +#include "php_variables.h" +#include "ext/standard/credits.h" +#ifdef PHP_WIN32 +#include +#include "win32/php_registry.h" +#include "ext/standard/flock_compat.h" +#endif +#include "php_syslog.h" +#include "Zend/zend_exceptions.h" + +#if PHP_SIGCHILD +#include +#include +#endif + +#include "zend_compile.h" +#include "zend_execute.h" +#include "zend_highlight.h" +#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" +#include "php_logos.h" +#include "php_streams.h" +#include "php_open_temporary_file.h" + +#include "SAPI.h" +#include "rfc1867.h" + +#if HAVE_MMAP || defined(PHP_WIN32) +# if HAVE_UNISTD_H +# include +# if defined(_SC_PAGESIZE) +# define REAL_PAGE_SIZE sysconf(_SC_PAGESIZE); +# elif defined(_SC_PAGE_SIZE) +# define REAL_PAGE_SIZE sysconf(_SC_PAGE_SIZE); +# endif +# endif +# if HAVE_SYS_MMAN_H +# include +# endif +# ifndef REAL_PAGE_SIZE +# ifdef PAGE_SIZE +# define REAL_PAGE_SIZE PAGE_SIZE +# else +# define REAL_PAGE_SIZE 4096 +# endif +# endif +#endif +/* }}} */ + +PHPAPI int (*php_register_internal_extensions_func)(TSRMLS_D) = php_register_internal_extensions; + +#ifndef ZTS +php_core_globals core_globals; +#else +PHPAPI int core_globals_id; +#endif + +#ifdef PHP_WIN32 +#include "win32_internal_function_disabled.h" + +static php_win32_disable_functions(TSRMLS_D) +{ + int i; + + if (EG(windows_version_info).dwMajorVersion < 5) { + for (i = 0; i < function_name_cnt_5; i++) { + if (zend_hash_del(CG(function_table), function_name_5[i], strlen(function_name_5[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_5[i]); + return FAILURE; + } + } + } + + if (EG(windows_version_info).dwMajorVersion < 6) { + for (i = 0; i < function_name_cnt_6; i++) { + if (zend_hash_del(CG(function_table), function_name_6[i], strlen(function_name_6[i]) + 1)==FAILURE) { + php_printf("Unable to disable function '%s'\n", function_name_6[i]); + return FAILURE; + } + } + } + return SUCCESS; +} +#endif + +#define SAFE_FILENAME(f) ((f)?(f):"-") + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnSetPrecision) +{ + int i = atoi(new_value); + if (i >= 0) { + EG(precision) = i; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMemoryLimit) +{ + if (new_value) { + PG(memory_limit) = zend_atol(new_value, new_value_length); + } else { + PG(memory_limit) = 1<<30; /* effectively, no limit */ + } + return zend_set_memory_limit(PG(memory_limit)); +} +/* }}} */ + + +/* {{{ php_disable_functions + */ +static void php_disable_functions(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_functions"))) { + return; + } + + e = PG(disable_functions) = strdup(INI_STR("disable_functions")); + if (e == NULL) { + return; + } + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_function(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_function(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_disable_classes + */ +static void php_disable_classes(TSRMLS_D) +{ + char *s = NULL, *e; + + if (!*(INI_STR("disable_classes"))) { + return; + } + + e = PG(disable_classes) = strdup(INI_STR("disable_classes")); + + while (*e) { + switch (*e) { + case ' ': + case ',': + if (s) { + *e = '\0'; + zend_disable_class(s, e-s TSRMLS_CC); + s = NULL; + } + break; + default: + if (!s) { + s = e; + } + break; + } + e++; + } + if (s) { + zend_disable_class(s, e-s TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ 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) +{ + if (stage==PHP_INI_STAGE_STARTUP) { + /* Don't set a timeout on startup, only per-request */ + EG(timeout_seconds) = atoi(new_value); + return SUCCESS; + } + zend_unset_timeout(TSRMLS_C); + EG(timeout_seconds) = atoi(new_value); + zend_set_timeout(EG(timeout_seconds), 0); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_get_display_errors_mode() helper function + */ +static int php_get_display_errors_mode(char *value, int value_length) +{ + int mode; + + if (!value) { + return PHP_DISPLAY_ERRORS_STDOUT; + } + + if (value_length == 2 && !strcasecmp("on", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 3 && !strcasecmp("yes", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 4 && !strcasecmp("true", value)) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else if (value_length == 6 && !strcasecmp(value, "stderr")) { + mode = PHP_DISPLAY_ERRORS_STDERR; + } else if (value_length == 6 && !strcasecmp(value, "stdout")) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } else { + mode = atoi(value); + if (mode && mode != PHP_DISPLAY_ERRORS_STDOUT && mode != PHP_DISPLAY_ERRORS_STDERR) { + mode = PHP_DISPLAY_ERRORS_STDOUT; + } + } + + return mode; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnUpdateDisplayErrors) +{ + PG(display_errors) = (zend_bool) php_get_display_errors_mode(new_value, new_value_length); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_INI_DISP + */ +static PHP_INI_DISP(display_errors_mode) +{ + int mode, tmp_value_length, cgi_or_cli; + char *tmp_value; + TSRMLS_FETCH(); + + if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { + tmp_value = (ini_entry->orig_value ? ini_entry->orig_value : NULL ); + tmp_value_length = ini_entry->orig_value_length; + } else if (ini_entry->value) { + tmp_value = ini_entry->value; + tmp_value_length = ini_entry->value_length; + } else { + tmp_value = NULL; + tmp_value_length = 0; + } + + mode = php_get_display_errors_mode(tmp_value, tmp_value_length); + + /* Display 'On' for other SAPIs instead of STDOUT or STDERR */ + cgi_or_cli = (!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")); + + switch (mode) { + case PHP_DISPLAY_ERRORS_STDERR: + if (cgi_or_cli ) { + PUTS("STDERR"); + } else { + PUTS("On"); + } + break; + + case PHP_DISPLAY_ERRORS_STDOUT: + if (cgi_or_cli ) { + PUTS("STDOUT"); + } else { + PUTS("On"); + } + break; + + default: + PUTS("Off"); + break; + } +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +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(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; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +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(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; +} +/* }}} */ + +/* {{{ PHP_INI_MH + */ +static PHP_INI_MH(OnChangeMailForceExtra) +{ + /* Don't allow changing it in htaccess */ + if (stage == PHP_INI_STAGE_HTACCESS) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +/* defined in browscap.c */ +PHP_INI_MH(OnChangeBrowscap); + + +/* Need to be read from the environment (?): + * PHP_AUTO_PREPEND_FILE + * PHP_AUTO_APPEND_FILE + * PHP_DOCUMENT_ROOT + * PHP_USER_DIR + * PHP_INCLUDE_PATH + */ + + /* Windows and Netware use the internal mail */ +#if defined(PHP_WIN32) || defined(NETWARE) +# define DEFAULT_SENDMAIL_PATH NULL +#elif defined(PHP_PROG_SENDMAIL) +# define DEFAULT_SENDMAIL_PATH PHP_PROG_SENDMAIL " -t -i " +#else +# define DEFAULT_SENDMAIL_PATH "/usr/sbin/sendmail -t -i" +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + 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("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) + STD_PHP_INI_BOOLEAN("enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_root", "", PHP_INI_ALL, OnUpdateString, docref_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("docref_ext", "", PHP_INI_ALL, OnUpdateString, docref_ext, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("html_errors", "1", PHP_INI_ALL, OnUpdateBool, html_errors, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("xmlrpc_errors", "0", PHP_INI_SYSTEM, OnUpdateBool, xmlrpc_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("xmlrpc_error_number", "0", PHP_INI_ALL, OnUpdateLong, xmlrpc_error_number, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_time", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, max_input_time, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_user_abort", "0", PHP_INI_ALL, OnUpdateBool, ignore_user_abort, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("implicit_flush", "0", PHP_INI_ALL, OnUpdateBool, implicit_flush, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("log_errors", "0", PHP_INI_ALL, OnUpdateBool, log_errors, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("log_errors_max_len", "1024", PHP_INI_ALL, OnUpdateLong, log_errors_max_len, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("ignore_repeated_errors", "0", PHP_INI_ALL, OnUpdateBool, ignore_repeated_errors, php_core_globals, core_globals) + 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_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("auto_globals_jit", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, auto_globals_jit, 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_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) + STD_PHP_INI_ENTRY("arg_separator.output", "&", PHP_INI_ALL, OnUpdateStringUnempty, arg_separator.output, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("arg_separator.input", "&", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, arg_separator.input, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("auto_append_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_append_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("auto_prepend_file", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, auto_prepend_file, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("doc_root", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, doc_root, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("default_charset", SAPI_DEFAULT_CHARSET, PHP_INI_ALL, OnUpdateString, default_charset, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("default_mimetype", SAPI_DEFAULT_MIMETYPE, PHP_INI_ALL, OnUpdateString, default_mimetype, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("error_log", NULL, PHP_INI_ALL, OnUpdateErrorLog, error_log, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("extension_dir", PHP_EXTENSION_DIR, PHP_INI_SYSTEM, OnUpdateStringUnempty, extension_dir, php_core_globals, core_globals) + 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_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) + STD_PHP_INI_ENTRY("post_max_size", "8M", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLong, post_max_size, sapi_globals_struct,sapi_globals) + STD_PHP_INI_ENTRY("upload_tmp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, upload_tmp_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_nesting_level", "64", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLongGEZero, max_input_nesting_level, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("max_input_vars", "1000", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateLongGEZero, max_input_vars, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("user_dir", NULL, PHP_INI_SYSTEM, OnUpdateString, user_dir, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("variables_order", "EGPCS", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStringUnempty, variables_order, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("request_order", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateString, request_order, php_core_globals, core_globals) + + STD_PHP_INI_ENTRY("error_append_string", NULL, PHP_INI_ALL, OnUpdateString, error_append_string, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("error_prepend_string", NULL, PHP_INI_ALL, OnUpdateString, error_prepend_string, php_core_globals, core_globals) + + PHP_INI_ENTRY("SMTP", "localhost",PHP_INI_ALL, NULL) + PHP_INI_ENTRY("smtp_port", "25", PHP_INI_ALL, NULL) + STD_PHP_INI_BOOLEAN("mail.add_x_header", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_x_header, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals) + PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap) + PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) + PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) + PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) + 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|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) + STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals) + + STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals) +#ifdef PHP_WIN32 + STD_PHP_INI_BOOLEAN("windows.show_crt_warning", "0", PHP_INI_ALL, OnUpdateBool, windows_show_crt_warning, php_core_globals, core_globals) +#endif +PHP_INI_END() +/* }}} */ + +/* True globals (no need for thread safety */ +/* But don't make them a single int bitfield */ +static int module_initialized = 0; +static int module_startup = 1; +static int module_shutdown = 0; + +/* {{{ php_during_module_startup */ +static int php_during_module_startup(void) +{ + return module_startup; +} +/* }}} */ + +/* {{{ php_during_module_shutdown */ +static int php_during_module_shutdown(void) +{ + return module_shutdown; +} +/* }}} */ + +/* {{{ 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) +{ + int fd = -1; + time_t error_time; + + if (PG(in_error_log)) { + /* prevent recursive invocation */ + return; + } + PG(in_error_log) = 1; + + /* Try to use the specified logging location. */ + if (PG(error_log) != NULL) { +#ifdef HAVE_SYSLOG_H + if (!strcmp(PG(error_log), "syslog")) { + php_syslog(LOG_NOTICE, "%s", log_message); + PG(in_error_log) = 0; + return; + } +#endif + fd = VCWD_OPEN_MODE(PG(error_log), O_CREAT | O_APPEND | O_WRONLY, 0644); + if (fd != -1) { + char *tmp; + int len; + char *error_time_str; + + time(&error_time); +#ifdef ZTS + if (!php_during_module_startup()) { + error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 1 TSRMLS_CC); + } else { + error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 0 TSRMLS_CC); + } +#else + error_time_str = php_format_date("d-M-Y H:i:s e", 13, error_time, 1 TSRMLS_CC); +#endif + len = spprintf(&tmp, 0, "[%s] %s%s", error_time_str, log_message, PHP_EOL); +#ifdef PHP_WIN32 + php_flock(fd, 2); +#endif + php_ignore_value(write(fd, tmp, len)); + efree(tmp); + efree(error_time_str); + close(fd); + PG(in_error_log) = 0; + return; + } + } + + /* Otherwise fall back to the default logging location, if we have one */ + + if (sapi_module.log_message) { + sapi_module.log_message(log_message TSRMLS_CC); + } + PG(in_error_log) = 0; +} +/* }}} */ + +/* {{{ php_write + wrapper for modules to use PHPWRITE */ +PHPAPI int php_write(void *buf, uint size TSRMLS_DC) +{ + return PHPWRITE(buf, size); +} +/* }}} */ + +/* {{{ php_printf + */ +PHPAPI int php_printf(const char *format, ...) +{ + va_list args; + int ret; + char *buffer; + int size; + TSRMLS_FETCH(); + + va_start(args, format); + size = vspprintf(&buffer, 0, format, args); + ret = PHPWRITE(buffer, size); + efree(buffer); + va_end(args); + + return ret; +} +/* }}} */ + +/* {{{ php_verror */ +/* php_verror is called from php_error_docref functions. + * Its purpose is to unify error messages and automatically generate clickable + * html error messages if correcponding ini setting (html_errors) is activated. + * See: CODING_STANDARDS for details. + */ +PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) +{ + char *buffer = NULL, *docref_buf = NULL, *target = NULL; + char *docref_target = "", *docref_root = ""; + char *p; + int buffer_len = 0; + const char *space = ""; + const char *class_name = ""; + const char *function; + int origin_len; + char *origin; + char *message; + int is_function = 0; + + /* get error text into buffer and escape for html if necessary */ + buffer_len = vspprintf(&buffer, 0, format, args); + + if (PG(html_errors)) { + size_t len; + char *replace = php_escape_html_entities(buffer, buffer_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(buffer); + buffer = replace; + buffer_len = len; + } + + /* which function caused the problem if any at all */ + if (php_during_module_startup()) { + function = "PHP Startup"; + } else if (php_during_module_shutdown()) { + function = "PHP Shutdown"; + } else if (EG(current_execute_data) && + EG(current_execute_data)->opline && + EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL + ) { + switch (EG(current_execute_data)->opline->extended_value) { + case ZEND_EVAL: + function = "eval"; + is_function = 1; + break; + case ZEND_INCLUDE: + function = "include"; + is_function = 1; + break; + case ZEND_INCLUDE_ONCE: + function = "include_once"; + is_function = 1; + break; + case ZEND_REQUIRE: + function = "require"; + is_function = 1; + break; + case ZEND_REQUIRE_ONCE: + function = "require_once"; + is_function = 1; + break; + default: + function = "Unknown"; + } + } else { + function = get_active_function_name(TSRMLS_C); + if (!function || !strlen(function)) { + function = "Unknown"; + } else { + is_function = 1; + class_name = get_active_class_name(&space TSRMLS_CC); + } + } + + /* if we still have memory then format the origin */ + if (is_function) { + origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, params); + } else { + origin_len = spprintf(&origin, 0, "%s", function); + } + + if (PG(html_errors)) { + size_t len; + char *replace = php_escape_html_entities(origin, origin_len, &len, 0, ENT_COMPAT, NULL TSRMLS_CC); + efree(origin); + origin = replace; + } + + /* origin and buffer available, so lets come up with the error message */ + if (docref && docref[0] == '#') { + docref_target = strchr(docref, '#'); + docref = NULL; + } + + /* no docref given but function is known (the default) */ + if (!docref && is_function) { + int doclen; + if (space[0] == '\0') { + doclen = spprintf(&docref_buf, 0, "function.%s", function); + } else { + doclen = spprintf(&docref_buf, 0, "%s.%s", class_name, function); + } + while((p = strchr(docref_buf, '_')) != NULL) { + *p = '-'; + } + docref = php_strtolower(docref_buf, doclen); + } + + /* we have a docref for a function AND + * - 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 (strncmp(docref, "http://", 7)) { + /* We don't have 'http://' so we use docref_root */ + + char *ref; /* temp copy for duplicated docref */ + + docref_root = PG(docref_root); + + ref = estrdup(docref); + if (docref_buf) { + efree(docref_buf); + } + docref_buf = ref; + /* strip of the target if any */ + p = strrchr(ref, '#'); + if (p) { + target = estrdup(p); + if (target) { + docref_target = target; + *p = '\0'; + } + } + /* add the extension if it is set in ini */ + if (PG(docref_ext) && strlen(PG(docref_ext))) { + spprintf(&docref_buf, 0, "%s%s", ref, PG(docref_ext)); + efree(ref); + } + docref = docref_buf; + } + /* display html formatted or only show the additional links */ + if (PG(html_errors)) { + spprintf(&message, 0, "%s [%s]: %s", origin, docref_root, docref, docref_target, docref, buffer); + } else { + spprintf(&message, 0, "%s [%s%s%s]: %s", origin, docref_root, docref, docref_target, buffer); + } + if (target) { + efree(target); + } + } else { + spprintf(&message, 0, "%s: %s", origin, buffer); + } + efree(origin); + if (docref_buf) { + efree(docref_buf); + } + + if (PG(track_errors) && module_initialized && + (!EG(user_error_handler) || !(EG(user_error_handler_error_reporting) & type))) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) &tmp, sizeof(zval *), NULL); + } + } + efree(buffer); + + php_error(type, "%s", message); + efree(message); +} +/* }}} */ + +/* {{{ php_error_docref0 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, "", type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref1 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...) +{ + va_list args; + + va_start(args, format); + php_verror(docref, param1, type, format, args TSRMLS_CC); + va_end(args); +} +/* }}} */ + +/* {{{ php_error_docref2 */ +/* See: CODING_STANDARDS for details. */ +PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...) +{ + char *params; + va_list args; + + spprintf(¶ms, 0, "%s,%s", param1, param2); + va_start(args, format); + php_verror(docref, params ? params : "...", type, format, args TSRMLS_CC); + va_end(args); + if (params) { + efree(params); + } +} +/* }}} */ + +#ifdef PHP_WIN32 +#define PHP_WIN32_ERROR_MSG_BUFFER_SIZE 512 +PHPAPI void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2 TSRMLS_DC) { + if (error == 0) { + php_error_docref2(NULL TSRMLS_CC, param1, param2, E_WARNING, "%s", strerror(errno)); + } else { + char buf[PHP_WIN32_ERROR_MSG_BUFFER_SIZE + 1]; + int buf_len; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buf, PHP_WIN32_ERROR_MSG_BUFFER_SIZE, NULL); + buf_len = strlen(buf); + if (buf_len >= 2) { + buf[buf_len - 1] = '\0'; + buf[buf_len - 2] = '\0'; + } + php_error_docref2(NULL TSRMLS_CC, param1, param2, E_WARNING, "%s (code: %lu)", (char *)buf, error); + } +} +#undef PHP_WIN32_ERROR_MSG_BUFFER_SIZE +#endif + +/* {{{ php_html_puts */ +PHPAPI void php_html_puts(const char *str, uint size TSRMLS_DC) +{ + zend_html_puts(str, size TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_error_cb + extended error handling function */ +static void php_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + char *buffer; + int buffer_len, display; + TSRMLS_FETCH(); + + buffer_len = vspprintf(&buffer, PG(log_errors_max_len), format, args); + + /* check for repeated errors to be ignored */ + if (PG(ignore_repeated_errors) && PG(last_error_message)) { + /* no check for PG(last_error_file) is needed since it cannot + * be NULL if PG(last_error_message) is not NULL */ + if (strcmp(PG(last_error_message), buffer) + || (!PG(ignore_repeated_source) + && ((PG(last_error_lineno) != (int)error_lineno) + || strcmp(PG(last_error_file), error_filename)))) { + display = 1; + } else { + display = 0; + } + } else { + display = 1; + } + + /* 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; + } + if (PG(last_error_file)) { + free(PG(last_error_file)); + PG(last_error_file) = NULL; + } +#ifdef ZEND_SIGNALS + HANDLE_UNBLOCK_INTERRUPTIONS(); +#endif + if (!error_filename) { + error_filename = "Unknown"; + } + PG(last_error_type) = type; + PG(last_error_message) = strdup(buffer); + PG(last_error_file) = strdup(error_filename); + PG(last_error_lineno) = error_lineno; + } + + /* according to error handling mode, suppress error, throw exception or show it */ + if (EG(error_handling) != EH_NORMAL) { + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + case E_PARSE: + /* fatal errors are real errors and cannot be made exceptions */ + break; + case E_STRICT: + case E_DEPRECATED: + case E_USER_DEPRECATED: + /* for the sake of BC to old damaged code */ + break; + case E_NOTICE: + case E_USER_NOTICE: + /* notices are no errors and are not treated as such like E_WARNINGS */ + break; + default: + /* throw an exception if we are in EH_THROW mode + * but DO NOT overwrite a pending exception + */ + if (EG(error_handling) == EH_THROW && !EG(exception)) { + zend_throw_error_exception(EG(exception_class), buffer, 0, type TSRMLS_CC); + } + efree(buffer); + return; + } + } + + /* display/log the error if necessary */ + if (display && (EG(error_reporting) & type || (type & E_CORE)) + && (PG(log_errors) || PG(display_errors) || (!module_initialized))) { + char *error_type_str; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + error_type_str = "Fatal error"; + break; + case E_RECOVERABLE_ERROR: + error_type_str = "Catchable fatal error"; + break; + case E_WARNING: + case E_CORE_WARNING: + case E_COMPILE_WARNING: + case E_USER_WARNING: + error_type_str = "Warning"; + break; + case E_PARSE: + error_type_str = "Parse error"; + break; + case E_NOTICE: + case E_USER_NOTICE: + error_type_str = "Notice"; + break; + case E_STRICT: + error_type_str = "Strict Standards"; + break; + case E_DEPRECATED: + case E_USER_DEPRECATED: + error_type_str = "Deprecated"; + break; + default: + error_type_str = "Unknown error"; + break; + } + + if (!module_initialized || PG(log_errors)) { + char *log_buffer; +#ifdef PHP_WIN32 + if ((type == E_CORE_ERROR || type == E_CORE_WARNING) && PG(display_startup_errors)) { + MessageBox(NULL, buffer, error_type_str, MB_OK|ZEND_SERVICE_MB_STYLE); + } +#endif + spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %d", error_type_str, buffer, error_filename, error_lineno); + php_log_err(log_buffer TSRMLS_CC); + efree(log_buffer); + } + + if (PG(display_errors) && ((module_initialized && !PG(during_request_startup)) || (PG(display_startup_errors)))) { + if (PG(xmlrpc_errors)) { + php_printf("faultCode%ldfaultString%s:%s in %s on line %d", PG(xmlrpc_error_number), error_type_str, buffer, error_filename, error_lineno); + } else { + char *prepend_string = INI_STR("error_prepend_string"); + char *append_string = INI_STR("error_append_string"); + + if (PG(html_errors)) { + 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
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buf, error_filename, error_lineno, STR_PRINT(append_string)); + efree(buf); + } else { + php_printf("%s
\n%s: %s in %s on line %d
\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } else { + /* Write CLI/CGI errors to stderr if display_errors = "stderr" */ + if ((!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi")) && + 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); + fflush(stderr); +#else + fprintf(stderr, "%s: %s in %s on line %d\n", error_type_str, buffer, error_filename, error_lineno); +#endif + } else { + php_printf("%s\n%s: %s in %s on line %d\n%s", STR_PRINT(prepend_string), error_type_str, buffer, error_filename, error_lineno, STR_PRINT(append_string)); + } + } + } + } +#if ZEND_DEBUG + if (PG(report_zend_debug)) { + zend_bool trigger_break; + + switch (type) { + case E_ERROR: + case E_CORE_ERROR: + case E_COMPILE_ERROR: + case E_USER_ERROR: + trigger_break=1; + break; + default: + trigger_break=0; + break; + } + zend_output_debug_string(trigger_break, "%s(%d) : %s - %s", error_filename, error_lineno, error_type_str, buffer); + } +#endif + } + + /* Bail out if we can't recover */ + switch (type) { + case E_CORE_ERROR: + if(!module_initialized) { + /* bad error in module startup - no way we can live with this */ + exit(-2); + } + /* no break - intentionally */ + case E_ERROR: + case E_RECOVERABLE_ERROR: + case E_PARSE: + case E_COMPILE_ERROR: + case E_USER_ERROR: + { /* new block to allow variable definition */ + /* eval() errors do not affect exit_status or response code */ + zend_bool during_eval = (type == E_PARSE) && (EG(current_execute_data) && + EG(current_execute_data)->opline && + EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL && + EG(current_execute_data)->opline->extended_value == ZEND_EVAL); + if (!during_eval) { + EG(exit_status) = 255; + } + if (module_initialized) { + if (!PG(display_errors) && + !SG(headers_sent) && + SG(sapi_headers).http_response_code == 200 && + !during_eval + ) { + sapi_header_line ctr = {0}; + + ctr.line = "HTTP/1.0 500 Internal Server Error"; + 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) { + CG(parse_error) = 0; + } else { + /* restore memory limit */ + zend_set_memory_limit(PG(memory_limit)); + efree(buffer); + zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); + zend_bailout(); + return; + } + } + break; + } + } + + /* Log if necessary */ + if (!display) { + efree(buffer); + return; + } + + if (PG(track_errors) && module_initialized) { + if (!EG(active_symbol_table)) { + zend_rebuild_symbol_table(TSRMLS_C); + } + if (EG(active_symbol_table)) { + zval *tmp; + ALLOC_INIT_ZVAL(tmp); + ZVAL_STRINGL(tmp, buffer, buffer_len, 1); + zend_hash_update(EG(active_symbol_table), "php_errormsg", sizeof("php_errormsg"), (void **) & tmp, sizeof(zval *), NULL); + } + } + + efree(buffer); +} +/* }}} */ + +/* {{{ 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) +{ + long new_timeout; + char *new_timeout_str; + int new_timeout_strlen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) { + return; + } + + new_timeout_strlen = zend_spprintf(&new_timeout_str, 0, "%ld", new_timeout); + + if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), new_timeout_str, new_timeout_strlen, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) { + RETVAL_TRUE; + } else { + RETVAL_FALSE; + } + efree(new_timeout_str); +} +/* }}} */ + +/* {{{ php_fopen_wrapper_for_zend + */ +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", USE_PATH|IGNORE_URL_WIN|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE, opened_path); +} +/* }}} */ + +static void php_zend_stream_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_close((php_stream*)handle); +} +/* }}} */ + +static void php_zend_stream_mmap_closer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_mmap_unmap((php_stream*)handle); + php_zend_stream_closer(handle TSRMLS_CC); +} +/* }}} */ + +static size_t php_zend_stream_fsizer(void *handle TSRMLS_DC) /* {{{ */ +{ + php_stream_statbuf ssb; + if (php_stream_stat((php_stream*)handle, &ssb) == 0) { + return ssb.sb.st_size; + } + return 0; +} +/* }}} */ + +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, USE_PATH|REPORT_ERRORS|STREAM_OPEN_FOR_INCLUDE TSRMLS_CC); +} +/* }}} */ + +PHPAPI int php_stream_open_for_zend_ex(const char *filename, zend_file_handle *handle, int mode TSRMLS_DC) /* {{{ */ +{ + char *p; + size_t len, mapped_len; + php_stream *stream = php_stream_open_wrapper((char *)filename, "rb", mode, &handle->opened_path); + + if (stream) { +#if HAVE_MMAP || defined(PHP_WIN32) + size_t page_size = REAL_PAGE_SIZE; +#endif + + handle->filename = (char*)filename; + handle->free_filename = 0; + handle->handle.stream.handle = stream; + handle->handle.stream.reader = (zend_stream_reader_t)_php_stream_read; + handle->handle.stream.fsizer = php_zend_stream_fsizer; + handle->handle.stream.isatty = 0; + /* can we mmap immeadiately? */ + memset(&handle->handle.stream.mmap, 0, sizeof(handle->handle.stream.mmap)); + len = php_zend_stream_fsizer(stream TSRMLS_CC); + if (len != 0 +#if HAVE_MMAP || defined(PHP_WIN32) + && ((len - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD +#endif + && php_stream_mmap_possible(stream) + && (p = php_stream_mmap_range(stream, 0, len, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped_len)) != NULL) { + handle->handle.stream.closer = php_zend_stream_mmap_closer; + handle->handle.stream.mmap.buf = p; + handle->handle.stream.mmap.len = mapped_len; + handle->type = ZEND_HANDLE_MAPPED; + } else { + handle->handle.stream.closer = php_zend_stream_closer; + handle->type = ZEND_HANDLE_STREAM; + } + /* suppress warning if this stream is not explicitly closed */ + php_stream_auto_cleanup(stream); + + return SUCCESS; + } + return FAILURE; +} +/* }}} */ + +static char *php_resolve_path_for_zend(const char *filename, int filename_len TSRMLS_DC) /* {{{ */ +{ + return php_resolve_path(filename, filename_len, PG(include_path) TSRMLS_CC); +} +/* }}} */ + +/* {{{ php_get_configuration_directive_for_zend + */ +static int php_get_configuration_directive_for_zend(const char *name, uint name_length, zval *contents) +{ + zval *retval = cfg_get_entry(name, name_length); + + if (retval) { + *contents = *retval; + return SUCCESS; + } else { + return FAILURE; + } +} +/* }}} */ + +/* {{{ php_message_handler_for_zend + */ +static void php_message_handler_for_zend(long message, const void *data TSRMLS_DC) +{ + switch (message) { + case ZMSG_FAILED_INCLUDE_FOPEN: + php_error_docref("function.include" TSRMLS_CC, E_WARNING, "Failed opening '%s' for inclusion (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_REQUIRE_FOPEN: + php_error_docref("function.require" TSRMLS_CC, E_COMPILE_ERROR, "Failed opening required '%s' (include_path='%s')", php_strip_url_passwd((char *) data), STR_PRINT(PG(include_path))); + break; + case ZMSG_FAILED_HIGHLIGHT_FOPEN: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed opening '%s' for highlighting", php_strip_url_passwd((char *) data)); + break; + case ZMSG_MEMORY_LEAK_DETECTED: + case ZMSG_MEMORY_LEAK_REPEATED: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[1024]; + + if (message==ZMSG_MEMORY_LEAK_DETECTED) { + zend_leak_info *t = (zend_leak_info *) data; + + snprintf(memory_leak_buf, 512, "%s(%d) : Freeing 0x%.8lX (%zu bytes), script=%s\n", t->filename, t->lineno, (zend_uintptr_t)t->addr, t->size, SAFE_FILENAME(SG(request_info).path_translated)); + if (t->orig_filename) { + char relay_buf[512]; + + snprintf(relay_buf, 512, "%s(%d) : Actual location (location was relayed)\n", t->orig_filename, t->orig_lineno); + strlcat(memory_leak_buf, relay_buf, sizeof(memory_leak_buf)); + } + } else { + unsigned long leak_count = (zend_uintptr_t) data; + + snprintf(memory_leak_buf, 512, "Last leak repeated %ld time%s\n", leak_count, (leak_count>1?"s":"")); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_MEMORY_LEAKS_GRAND_TOTAL: +#if ZEND_DEBUG + if (EG(error_reporting) & E_WARNING) { + char memory_leak_buf[512]; + + snprintf(memory_leak_buf, 512, "=== Total %d memory leaks detected ===\n", *((zend_uint *) data)); +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } +#endif + break; + case ZMSG_LOG_SCRIPT_NAME: { + struct tm *ta, tmbuf; + time_t curtime; + char *datetime_str, asctimebuf[52]; + char memory_leak_buf[4096]; + + time(&curtime); + ta = php_localtime_r(&curtime, &tmbuf); + datetime_str = php_asctime_r(ta, asctimebuf); + if (datetime_str) { + datetime_str[strlen(datetime_str)-1]=0; /* get rid of the trailing newline */ + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[%s] Script: '%s'\n", datetime_str, SAFE_FILENAME(SG(request_info).path_translated)); + } else { + snprintf(memory_leak_buf, sizeof(memory_leak_buf), "[null] Script: '%s'\n", SAFE_FILENAME(SG(request_info).path_translated)); + } +# if defined(PHP_WIN32) + OutputDebugString(memory_leak_buf); +# else + fprintf(stderr, "%s", memory_leak_buf); +# endif + } + break; + } +} +/* }}} */ + + +void php_on_timeout(int seconds TSRMLS_DC) +{ + PG(connection_status) |= PHP_CONNECTION_TIMEOUT; + zend_set_timeout(EG(timeout_seconds), 1); + if(PG(exit_on_timeout)) sapi_terminate_process(TSRMLS_C); +} + +#if PHP_SIGCHILD +/* {{{ sigchld_handler + */ +static void sigchld_handler(int apar) +{ + int errno_save = errno; + + while (waitpid(-1, NULL, WNOHANG) > 0); + signal(SIGCHLD, sigchld_handler); + + errno = errno_save; +} +/* }}} */ +#endif + +/* {{{ php_start_sapi() + */ +static int php_start_sapi(TSRMLS_D) +{ + int retval = SUCCESS; + + if(!SG(sapi_started)) { + zend_try { + PG(during_request_startup) = 1; + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + + zend_activate(TSRMLS_C); + zend_set_timeout(EG(timeout_seconds), 1); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + } + return retval; +} + +/* }}} */ + +/* {{{ php_request_startup + */ +#ifndef APACHE_HOOKS +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 + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + zend_try { + PG(in_error_log) = 0; + PG(during_request_startup) = 1; + + php_output_activate(TSRMLS_C); + + /* initialize global variables */ + PG(modules_activated) = 0; + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(in_user_include) = 0; + + 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 an open_basedir is set */ + if (PG(open_basedir) && *PG(open_basedir)) { + CWDG(realpath_cache_size_limit) = 0; + } + + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + + if (PG(output_handler) && PG(output_handler)[0]) { + 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)) { + 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_output_set_implicit_flush(1 TSRMLS_CC); + } + + /* We turn this off in php_execute_script() */ + /* PG(during_request_startup) = 0; */ + + php_hash_environment(TSRMLS_C); + zend_activate_modules(TSRMLS_C); + PG(modules_activated)=1; + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + SG(sapi_started) = 1; + + return retval; +} +# else +int php_request_startup(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHILD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi() == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + zend_try { + PG(during_request_startup) = 1; + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); + } + } zend_catch { + retval = FAILURE; + } zend_end_try(); + + return retval; +} +# endif +/* }}} */ + +/* {{{ php_request_startup_for_hook + */ +int php_request_startup_for_hook(TSRMLS_D) +{ + int retval = SUCCESS; + +#if PHP_SIGCHLD + signal(SIGCHLD, sigchld_handler); +#endif + + if (php_start_sapi(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + php_output_activate(TSRMLS_C); + sapi_activate_headers_only(TSRMLS_C); + php_hash_environment(TSRMLS_C); + + return retval; +} +/* }}} */ + +/* {{{ php_request_shutdown_for_exec + */ +void php_request_shutdown_for_exec(void *dummy) +{ + TSRMLS_FETCH(); + + /* 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); +} +/* }}} */ + +/* {{{ php_request_shutdown_for_hook + */ +void php_request_shutdown_for_hook(void *dummy) +{ + TSRMLS_FETCH(); + + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + zend_try { + zend_unset_timeout(TSRMLS_C); + } zend_end_try(); + + zend_try { + int i; + + for (i = 0; i < NUM_TRACK_VARS; i++) { + if (PG(http_globals)[i]) { + zval_ptr_dtor(&PG(http_globals)[i]); + } + } + } zend_end_try(); + + zend_deactivate(TSRMLS_C); + + zend_try { + sapi_deactivate(TSRMLS_C); + } zend_end_try(); + + zend_try { + php_shutdown_stream_hashes(TSRMLS_C); + } zend_end_try(); + + zend_try { + shutdown_memory_manager(CG(unclean_shutdown), 0 TSRMLS_CC); + } zend_end_try(); + + zend_interned_strings_restore(TSRMLS_C); + +#ifdef ZEND_SIGNALS + zend_try { + zend_signal_deactivate(TSRMLS_C); + } zend_end_try(); +#endif +} + +/* }}} */ + +/* {{{ php_request_shutdown + */ +void php_request_shutdown(void *dummy) +{ + zend_bool report_memleaks; + TSRMLS_FETCH(); + + report_memleaks = PG(report_memleaks); + + /* EG(opline_ptr) points into nirvana and therefore cannot be safely accessed + * inside zend_executor callback functions. + */ + EG(opline_ptr) = NULL; + EG(active_op_array) = NULL; + + php_deactivate_ticks(TSRMLS_C); + + /* 1. Call all possible shutdown functions registered with register_shutdown_function() */ + if (PG(modules_activated)) zend_try { + php_call_shutdown_functions(TSRMLS_C); + } zend_end_try(); + + /* 2. Call all possible __destruct() functions */ + zend_try { + zend_call_destructors(TSRMLS_C); + } zend_end_try(); + + /* 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 && + (size_t)PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC) + ) { + send_buffer = 0; + } + + if (!send_buffer) { + php_output_discard_all(TSRMLS_C); + } else { + php_output_end_all(TSRMLS_C); + } + } zend_end_try(); + + /* 4. Reset max_execution_time (no longer executing php code after response sent) */ + zend_try { + zend_unset_timeout(TSRMLS_C); + } zend_end_try(); + + /* 5. Call all extensions RSHUTDOWN functions */ + if (PG(modules_activated)) { + zend_deactivate_modules(TSRMLS_C); + php_free_shutdown_functions(TSRMLS_C); + } + + /* 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; + + for (i=0; ilast_error_message) { + free(core_globals->last_error_message); + } + if (core_globals->last_error_file) { + free(core_globals->last_error_file); + } + if (core_globals->disable_functions) { + free(core_globals->disable_functions); + } + 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); +} +/* }}} */ + +PHP_MINFO_FUNCTION(php_core) { /* {{{ */ + php_info_print_table_start(); + php_info_print_table_row(2, "PHP Version", PHP_VERSION); + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +/* {{{ php_register_extensions + */ +int php_register_extensions(zend_module_entry **ptr, int count TSRMLS_DC) +{ + zend_module_entry **end = ptr + count; + + while (ptr < end) { + if (*ptr) { + if (zend_register_internal_module(*ptr TSRMLS_CC)==NULL) { + return FAILURE; + } + } + ptr++; + } + return SUCCESS; +} +/* }}} */ + +#if defined(PHP_WIN32) && _MSC_VER >= 1400 +static _invalid_parameter_handler old_invalid_parameter_handler; + +void dummy_invalid_parameter_handler( + const wchar_t *expression, + const wchar_t *function, + const wchar_t *file, + unsigned int line, + uintptr_t pEwserved) +{ + static int called = 0; + char buf[1024]; + int len; + + if (!called) { + TSRMLS_FETCH(); + if(PG(windows_show_crt_warning)) { + called = 1; + if (function) { + if (file) { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws' (%ws:%d)", function, file, line); + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid parameter detected in CRT function '%ws'", function); + } + } else { + len = _snprintf(buf, sizeof(buf)-1, "Invalid CRT parameter detected (function not known)"); + } + zend_error(E_WARNING, "%s", buf); + called = 0; + } + } +} +#endif + +/* {{{ php_module_startup + */ +int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules) +{ + zend_utility_functions zuf; + zend_utility_values zuv; + int retval = SUCCESS, module_number=0; /* for REGISTER_INI_ENTRIES() */ + char *php_os; + zend_module_entry *module; +#ifdef ZTS + zend_executor_globals *executor_globals; + 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 + 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) { + _set_invalid_parameter_handler(old_invalid_parameter_handler); + } + + /* Disable the message box for assertions.*/ + _CrtSetReportMode(_CRT_ASSERT, 0); +#endif +#else + php_os=PHP_OS; +#endif + +#ifdef ZTS + tsrm_ls = ts_resource(0); +#endif + +#ifdef PHP_WIN32 + php_win32_init_rng_lock(); +#endif + + module_shutdown = 0; + module_startup = 1; + sapi_initialize_empty_request(TSRMLS_C); + sapi_activate(TSRMLS_C); + + if (module_initialized) { + return SUCCESS; + } + + sapi_module = *sf; + + php_output_startup(); + + zuf.error_function = php_error_cb; + zuf.printf_function = php_printf; + 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; + zuf.unblock_interruptions = sapi_module.unblock_interruptions; + zuf.get_configuration_directive = php_get_configuration_directive_for_zend; + zuf.ticks_function = php_run_ticks; + zuf.on_timeout = php_on_timeout; + zuf.stream_open_function = php_stream_open_for_zend; + zuf.vspprintf_function = vspprintf; + zuf.getenv_function = sapi_getenv; + zuf.resolve_path_function = php_resolve_path_for_zend; + zend_startup(&zuf, NULL TSRMLS_CC); + +#ifdef ZTS + executor_globals = ts_resource(executor_globals_id); + ts_allocate_id(&core_globals_id, sizeof(php_core_globals), (ts_allocate_ctor) core_globals_ctor, (ts_allocate_dtor) core_globals_dtor); + core_globals = ts_resource(core_globals_id); +#ifdef PHP_WIN32 + ts_allocate_id(&php_win32_core_globals_id, sizeof(php_win32_core_globals), (ts_allocate_ctor) php_win32_core_globals_ctor, (ts_allocate_dtor) php_win32_core_globals_dtor); +#endif +#else + php_startup_ticks(TSRMLS_C); +#endif + gc_globals_ctor(TSRMLS_C); + +#ifdef PHP_WIN32 + { + OSVERSIONINFOEX *osvi = &EG(windows_version_info); + + ZeroMemory(osvi, sizeof(OSVERSIONINFOEX)); + osvi->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if( !GetVersionEx((OSVERSIONINFO *) osvi)) { + php_printf("\nGetVersionEx unusable. %d\n", GetLastError()); + return FAILURE; + } + } +#endif + EG(bailout) = NULL; + EG(error_reporting) = E_ALL & ~E_NOTICE; + EG(active_symbol_table) = NULL; + PG(header_is_being_sent) = 0; + SG(request_info).headers_only = 0; + SG(request_info).argv0 = NULL; + SG(request_info).argc=0; + SG(request_info).argv=(char **)NULL; + PG(connection_status) = PHP_CONNECTION_NORMAL; + PG(during_request_startup) = 0; + PG(last_error_message) = NULL; + PG(last_error_file) = NULL; + PG(last_error_lineno) = 0; + EG(error_handling) = EH_NORMAL; + EG(exception_class) = NULL; + PG(disable_functions) = NULL; + PG(disable_classes) = NULL; + +#if HAVE_SETLOCALE + setlocale(LC_CTYPE, ""); + zend_update_current_locale(); +#endif + +#if HAVE_TZSET + tzset(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /* start up winsock services */ + if (WSAStartup(wVersionRequested, &wsaData) != 0) { + php_printf("\nwinsock.dll unusable. %d\n", WSAGetLastError()); + return FAILURE; + } +#endif + + le_index_ptr = zend_register_list_destructors_ex(NULL, NULL, "index pointer", 0); + + /* Register constants */ + REGISTER_MAIN_STRINGL_CONSTANT("PHP_VERSION", PHP_VERSION, sizeof(PHP_VERSION)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MAJOR_VERSION", PHP_MAJOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_MINOR_VERSION", PHP_MINOR_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_RELEASE_VERSION", PHP_RELEASE_VERSION, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTRA_VERSION", PHP_EXTRA_VERSION, sizeof(PHP_EXTRA_VERSION) - 1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_VERSION_ID", PHP_VERSION_ID, CONST_PERSISTENT | CONST_CS); +#ifdef ZTS + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 1, CONST_PERSISTENT | CONST_CS); +#else + REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 0, CONST_PERSISTENT | CONST_CS); +#endif + REGISTER_MAIN_LONG_CONSTANT("PHP_DEBUG", PHP_DEBUG, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_OS", php_os, strlen(php_os), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SAPI", sapi_module.name, strlen(sapi_module.name), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("DEFAULT_INCLUDE_PATH", PHP_INCLUDE_PATH, sizeof(PHP_INCLUDE_PATH)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_INSTALL_DIR", PEAR_INSTALLDIR, sizeof(PEAR_INSTALLDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PEAR_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_PREFIX", PHP_PREFIX, sizeof(PHP_PREFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINDIR", PHP_BINDIR, sizeof(PHP_BINDIR)-1, CONST_PERSISTENT | CONST_CS); +#ifndef PHP_WIN32 + REGISTER_MAIN_STRINGL_CONSTANT("PHP_MANDIR", PHP_MANDIR, sizeof(PHP_MANDIR)-1, CONST_PERSISTENT | CONST_CS); +#endif + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LIBDIR", PHP_LIBDIR, sizeof(PHP_LIBDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_DATADIR", PHP_DATADIR, sizeof(PHP_DATADIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SYSCONFDIR", PHP_SYSCONFDIR, sizeof(PHP_SYSCONFDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_LOCALSTATEDIR", PHP_LOCALSTATEDIR, sizeof(PHP_LOCALSTATEDIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, strlen(PHP_CONFIG_FILE_PATH), CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR, sizeof(PHP_CONFIG_FILE_SCAN_DIR)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_SHLIB_SUFFIX", PHP_SHLIB_SUFFIX, sizeof(PHP_SHLIB_SUFFIX)-1, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS); + 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 PHP_WIN32 + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MINOR", EG(windows_version_info).dwMinorVersion, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_BUILD", EG(windows_version_info).dwBuildNumber, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PLATFORM", EG(windows_version_info).dwPlatformId, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MAJOR", EG(windows_version_info).wServicePackMajor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MINOR", EG(windows_version_info).wServicePackMinor, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SUITEMASK", EG(windows_version_info).wSuiteMask, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PRODUCTTYPE", EG(windows_version_info).wProductType, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_DOMAIN_CONTROLLER", VER_NT_DOMAIN_CONTROLLER, CONST_PERSISTENT | CONST_CS); + REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_SERVER", VER_NT_SERVER, CONST_PERSISTENT | CONST_CS); + 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); + + /* this will read in php.ini, set up the configuration parameters, + load zend extensions and register php function extensions + to be loaded later */ + if (php_init_config(TSRMLS_C) == FAILURE) { + return FAILURE; + } + + /* Register PHP core ini entries */ + REGISTER_INI_ENTRIES(); + + /* Register Zend ini entries */ + zend_register_standard_ini_entries(TSRMLS_C); + + /* Disable realpath cache if an open_basedir is set */ + if (PG(open_basedir) && *PG(open_basedir)) { + CWDG(realpath_cache_size_limit) = 0; + } + + /* initialize stream wrappers registry + * (this uses configuration parameters from php.ini) + */ + if (php_init_stream_wrappers(module_number TSRMLS_CC) == FAILURE) { + php_printf("PHP: Unable to initialize stream url wrappers.\n"); + return FAILURE; + } + + /* initialize registry for images to be used in phpinfo() + (this uses configuration parameters from php.ini) + */ + if (php_init_info_logos() == FAILURE) { + php_printf("PHP: Unable to initialize info phpinfo logos.\n"); + return FAILURE; + } + + zuv.html_errors = 1; + zuv.import_use_extension = ".php"; + php_startup_auto_globals(TSRMLS_C); + zend_set_utility_values(&zuv); + php_startup_sapi_content_types(TSRMLS_C); + + /* startup extensions staticly compiled in */ + if (php_register_internal_extensions_func(TSRMLS_C) == FAILURE) { + php_printf("Unable to start builtin modules\n"); + return FAILURE; + } + + /* start additional PHP extensions */ + php_register_extensions(&additional_modules, num_additional_modules TSRMLS_CC); + + /* load and startup extensions compiled as shared objects (aka DLLs) + as requested by php.ini entries + theese are loaded after initialization of internal extensions + as extensions *might* rely on things from ext/standard + which is always an internal extension and to be initialized + ahead of all other internals + */ + php_ini_register_extensions(TSRMLS_C); + zend_startup_modules(TSRMLS_C); + + /* 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) { + EG(current_module) = module; + zend_register_functions(NULL, sapi_module.additional_functions, NULL, MODULE_PERSISTENT TSRMLS_CC); + EG(current_module) = NULL; + } + } + + /* disable certain classes and functions as requested by php.ini */ + php_disable_functions(TSRMLS_C); + php_disable_classes(TSRMLS_C); + + /* make core report what it should */ + if (zend_hash_find(&module_registry, "core", sizeof("core"), (void**)&module)==SUCCESS) { + module->version = PHP_VERSION; + module->info_func = PHP_MINFO(php_core); + } + + +#ifdef PHP_WIN32 + /* Disable incompatible functions for the running platform */ + if (php_win32_disable_functions(TSRMLS_C) == FAILURE) { + php_printf("Unable to disable unsupported functions\n"); + return FAILURE; + } +#endif + +#ifdef ZTS + zend_post_startup(TSRMLS_C); +#endif + + module_initialized = 1; + + /* Check for deprecated directives */ + /* NOTE: If you add anything here, remember to add it to Makefile.global! */ + { + 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 + } + } + }; + + unsigned int i; + + zend_try { + /* 2 = Count of deprecation structs */ + for (i = 0; i < 2; i++) { + const char **p = directives[i].directives; + + 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 retval; +} +/* }}} */ + +void php_module_shutdown_for_exec(void) +{ + /* used to close fd's in the range 3.255 here, but it's problematic */ +} + +/* {{{ php_module_shutdown_wrapper + */ +int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals) +{ + TSRMLS_FETCH(); + php_module_shutdown(TSRMLS_C); + return SUCCESS; +} +/* }}} */ + +/* {{{ php_module_shutdown + */ +void php_module_shutdown(TSRMLS_D) +{ + int module_number=0; /* for UNREGISTER_INI_ENTRIES() */ + + module_shutdown = 1; + + if (!module_initialized) { + return; + } + +#ifdef ZTS + ts_free_worker_threads(); +#endif + +#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK)) + /*close winsock */ + WSACleanup(); +#endif + +#ifdef PHP_WIN32 + php_win32_free_rng_lock(); +#endif + + sapi_flush(TSRMLS_C); + + zend_shutdown(TSRMLS_C); + + /* Destroys filter & transport registries too */ + php_shutdown_stream_wrappers(module_number TSRMLS_CC); + + php_shutdown_info_logos(); + UNREGISTER_INI_ENTRIES(); + + /* close down the ini config */ + php_shutdown_config(); + +#ifndef ZTS + zend_ini_shutdown(TSRMLS_C); + shutdown_memory_manager(CG(unclean_shutdown), 1 TSRMLS_CC); +#else + zend_ini_global_shutdown(TSRMLS_C); +#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); + } +#endif +} +/* }}} */ + +/* {{{ php_execute_script + */ +PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC) +{ + zend_file_handle *prepend_file_p, *append_file_p; + zend_file_handle prepend_file = {0}, append_file = {0}; +#if HAVE_BROKEN_GETCWD + int old_cwd_fd = -1; +#else + char *old_cwd; + ALLOCA_FLAG(use_heap) +#endif + int retval = 0; + + EG(exit_status) = 0; + if (php_handle_special_queries(TSRMLS_C)) { + zend_file_handle_dtor(primary_file TSRMLS_CC); + return 0; + } +#ifndef HAVE_BROKEN_GETCWD +# define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; +#endif + + zend_try { + char realfile[MAXPATHLEN]; + +#ifdef PHP_WIN32 + if(primary_file->filename) { + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); + } +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { +#if HAVE_BROKEN_GETCWD + /* this looks nasty to me */ + old_cwd_fd = open(".", 0); +#else + php_ignore_value(VCWD_GETCWD(old_cwd, OLD_CWD_SIZE-1)); +#endif + VCWD_CHDIR_FILE(primary_file->filename); + } + + /* Only lookup the real file path and add it to the included_files list if already opened + * otherwise it will get opened and added to the included_files list in zend_execute_scripts + */ + if (primary_file->filename && + (primary_file->filename[0] != '-' || primary_file->filename[1] != 0) && + primary_file->opened_path == NULL && + primary_file->type != ZEND_HANDLE_FILENAME + ) { + int realfile_len; + int dummy = 1; + + if (expand_filepath(primary_file->filename, realfile TSRMLS_CC)) { + realfile_len = strlen(realfile); + zend_hash_add(&EG(included_files), realfile, realfile_len+1, (void *)&dummy, sizeof(int), NULL); + primary_file->opened_path = estrndup(realfile, realfile_len); + } + } + + if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) { + prepend_file.filename = PG(auto_prepend_file); + prepend_file.opened_path = NULL; + prepend_file.free_filename = 0; + prepend_file.type = ZEND_HANDLE_FILENAME; + prepend_file_p = &prepend_file; + } else { + prepend_file_p = NULL; + } + + if (PG(auto_append_file) && PG(auto_append_file)[0]) { + append_file.filename = PG(auto_append_file); + append_file.opened_path = NULL; + append_file.free_filename = 0; + append_file.type = ZEND_HANDLE_FILENAME; + append_file_p = &append_file; + } else { + append_file_p = NULL; + } + if (PG(max_input_time) != -1) { +#ifdef PHP_WIN32 + zend_unset_timeout(TSRMLS_C); +#endif + zend_set_timeout(INI_INT("max_execution_time"), 0); + } + retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS); + + } zend_end_try(); + +#if HAVE_BROKEN_GETCWD + if (old_cwd_fd != -1) { + fchdir(old_cwd_fd); + close(old_cwd_fd); + } +#else + if (old_cwd[0] != '\0') { + php_ignore_value(VCWD_CHDIR(old_cwd)); + } + free_alloca(old_cwd, use_heap); +#endif + return retval; +} +/* }}} */ + +/* {{{ php_execute_simple_script + */ +PHPAPI int php_execute_simple_script(zend_file_handle *primary_file, zval **ret TSRMLS_DC) +{ + char *old_cwd; + ALLOCA_FLAG(use_heap) + + EG(exit_status) = 0; +#define OLD_CWD_SIZE 4096 + old_cwd = do_alloca(OLD_CWD_SIZE, use_heap); + old_cwd[0] = '\0'; + + zend_try { +#ifdef PHP_WIN32 + if(primary_file->filename) { + UpdateIniFromRegistry(primary_file->filename TSRMLS_CC); + } +#endif + + PG(during_request_startup) = 0; + + if (primary_file->filename && !(SG(options) & SAPI_OPTION_NO_CHDIR)) { + 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') { + php_ignore_value(VCWD_CHDIR(old_cwd)); + } + + free_alloca(old_cwd, use_heap); + return EG(exit_status); +} +/* }}} */ + +/* {{{ php_handle_aborted_connection + */ +PHPAPI void php_handle_aborted_connection(void) +{ + TSRMLS_FETCH(); + + PG(connection_status) = PHP_CONNECTION_ABORTED; + php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); + + if (!PG(ignore_user_abort)) { + zend_bailout(); + } +} +/* }}} */ + +/* {{{ php_handle_auth_data + */ +PHPAPI int php_handle_auth_data(const char *auth TSRMLS_DC) +{ + int ret = -1; + + if (auth && auth[0] != '\0' && strncmp(auth, "Basic ", 6) == 0) { + char *pass; + char *user; + + user = php_base64_decode(auth + 6, strlen(auth) - 6, NULL); + if (user) { + pass = strchr(user, ':'); + if (pass) { + *pass++ = '\0'; + SG(request_info).auth_user = user; + SG(request_info).auth_password = estrdup(pass); + ret = 0; + } else { + efree(user); + } + } + } + + if (ret == -1) { + SG(request_info).auth_user = SG(request_info).auth_password = NULL; + } else { + SG(request_info).auth_digest = NULL; + } + + if (ret == -1 && auth && auth[0] != '\0' && strncmp(auth, "Digest ", 7) == 0) { + SG(request_info).auth_digest = estrdup(auth + 7); + ret = 0; + } + + if (ret == -1) { + SG(request_info).auth_digest = NULL; + } + + return ret; +} +/* }}} */ + +/* {{{ php_lint_script + */ +PHPAPI int php_lint_script(zend_file_handle *file TSRMLS_DC) +{ + zend_op_array *op_array; + int retval = FAILURE; + + zend_try { + op_array = zend_compile_file(file, ZEND_INCLUDE TSRMLS_CC); + zend_destroy_file_handle(file TSRMLS_CC); + + if (op_array) { + destroy_op_array(op_array TSRMLS_CC); + efree(op_array); + retval = SUCCESS; + } + } zend_end_try(); + + return retval; +} +/* }}} */ + +#ifdef PHP_WIN32 +/* {{{ dummy_indent + just so that this symbol gets exported... */ +PHPAPI void dummy_indent(void) +{ + zend_indent(); +} +/* }}} */ +#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/mergesort.c b/main/mergesort.c new file mode 100644 index 0000000..4555e1b --- /dev/null +++ b/main/mergesort.c @@ -0,0 +1,358 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Peter McIlroy. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)merge.c 8.2 (Berkeley) 2/14/94"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Hybrid exponential search/linear search merge sort with hybrid + * natural/pairwise first pass. Requires about .3% more comparisons + * for random data than LSMS with pairwise first pass alone. + * It works for objects as small as two bytes. + */ + +#define NATURAL +#define THRESHOLD 16 /* Best choice for natural merge cut-off. */ + +/* #define NATURAL to get hybrid natural merge. + * (The default is pairwise merging.) + */ + +#include + +#include +#include +#include + +#include "php.h" + +#ifdef PHP_WIN32 +#include /* Includes definition for u_char */ +#endif + +static void setup(u_char *list1, u_char *list2, size_t n, size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC); +static void insertionsort(u_char *a, size_t n, size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC); + +#define ISIZE sizeof(int) +#define PSIZE sizeof(u_char *) +#define ICOPY_LIST(src, dst, last) \ + do \ + *(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \ + while(src < last) +#define ICOPY_ELT(src, dst, i) \ + do \ + *(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \ + while (i -= ISIZE) + +#define CCOPY_LIST(src, dst, last) \ + do \ + *dst++ = *src++; \ + while (src < last) +#define CCOPY_ELT(src, dst, i) \ + do \ + *dst++ = *src++; \ + while (i -= 1) + +/* + * Find the next possible pointer head. (Trickery for forcing an array + * to do double duty as a linked list when objects do not align with word + * boundaries. + */ +/* Assumption: PSIZE is a power of 2. */ +#define EVAL(p) (u_char **) \ + ((u_char *)0 + \ + (((u_char *)p + PSIZE - 1 - (u_char *) 0) & ~(PSIZE - 1))) + +/* {{{ php_mergesort + * Arguments are as for qsort. + */ +PHPAPI int php_mergesort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC) +{ + register unsigned int i; + register int sense; + int big, iflag; + register u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; + u_char *list2, *list1, *p2, *p, *last, **p1; + + if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ + errno = EINVAL; + return (-1); + } + + if (nmemb == 0) + return (0); + + /* + * XXX + * Stupid subtraction for the Cray. + */ + iflag = 0; + if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) + iflag = 1; + + if ((list2 = malloc(nmemb * size + PSIZE)) == NULL) + return (-1); + + list1 = base; + setup(list1, list2, nmemb, size, cmp TSRMLS_CC); + last = list2 + nmemb * size; + i = big = 0; + while (*EVAL(list2) != last) { + l2 = list1; + p1 = EVAL(list1); + for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { + p2 = *EVAL(p2); + f1 = l2; + f2 = l1 = list1 + (p2 - list2); + if (p2 != last) + p2 = *EVAL(p2); + l2 = list1 + (p2 - list2); + while (f1 < l1 && f2 < l2) { + if ((*cmp)(f1, f2 TSRMLS_CC) <= 0) { + q = f2; + b = f1, t = l1; + sense = -1; + } else { + q = f1; + b = f2, t = l2; + sense = 0; + } + if (!big) { /* here i = 0 */ + while ((b += size) < t && cmp(q, b TSRMLS_CC) >sense) + if (++i == 6) { + big = 1; + goto EXPONENTIAL; + } + } else { +EXPONENTIAL: for (i = size; ; i <<= 1) + if ((p = (b + i)) >= t) { + if ((p = t - size) > b && + (*cmp)(q, p TSRMLS_CC) <= sense) + t = p; + else + b = p; + break; + } else if ((*cmp)(q, p TSRMLS_CC) <= sense) { + t = p; + if (i == size) + big = 0; + goto FASTCASE; + } else + b = p; + while (t > b+size) { + i = (((t - b) / size) >> 1) * size; + if ((*cmp)(q, p = b + i TSRMLS_CC) <= sense) + t = p; + else + b = p; + } + goto COPY; +FASTCASE: while (i > size) + if ((*cmp)(q, + p = b + (i >>= 1) TSRMLS_CC) <= sense) + t = p; + else + b = p; +COPY: b = t; + } + i = size; + if (q == f1) { + if (iflag) { + ICOPY_LIST(f2, tp2, b); + ICOPY_ELT(f1, tp2, i); + } else { + CCOPY_LIST(f2, tp2, b); + CCOPY_ELT(f1, tp2, i); + } + } else { + if (iflag) { + ICOPY_LIST(f1, tp2, b); + ICOPY_ELT(f2, tp2, i); + } else { + CCOPY_LIST(f1, tp2, b); + CCOPY_ELT(f2, tp2, i); + } + } + } + if (f2 < l2) { + if (iflag) + ICOPY_LIST(f2, tp2, l2); + else + CCOPY_LIST(f2, tp2, l2); + } else if (f1 < l1) { + if (iflag) + ICOPY_LIST(f1, tp2, l1); + else + CCOPY_LIST(f1, tp2, l1); + } + *p1 = l2; + } + tp2 = list1; /* swap list1, list2 */ + list1 = list2; + list2 = tp2; + last = list2 + nmemb*size; + } + if (base == list2) { + memmove(list2, list1, nmemb*size); + list2 = list1; + } + free(list2); + return (0); +} +/* }}} */ + +#define swap(a, b) { \ + s = b; \ + i = size; \ + do { \ + tmp = *a; *a++ = *s; *s++ = tmp; \ + } while (--i); \ + a -= size; \ + } +#define reverse(bot, top) { \ + s = top; \ + do { \ + i = size; \ + do { \ + tmp = *bot; *bot++ = *s; *s++ = tmp; \ + } while (--i); \ + s -= size2; \ + } while(bot < s); \ +} + +/* {{{ setup + * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of + * increasing order, list2 in a corresponding linked list. Checks for runs + * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL + * is defined. Otherwise simple pairwise merging is used.) + */ +static void setup(u_char *list1, u_char *list2, size_t n, size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC) +{ + int i, length, size2, tmp, sense; + u_char *f1, *f2, *s, *l2, *last, *p2; + + size2 = size*2; + if (n <= 5) { + insertionsort(list1, n, size, cmp TSRMLS_CC); + *EVAL(list2) = (u_char*) list2 + n*size; + return; + } + /* + * Avoid running pointers out of bounds; limit n to evens + * for simplicity. + */ + i = 4 + (n & 1); + insertionsort(list1 + (n - i) * size, i, size, cmp TSRMLS_CC); + last = list1 + size * (n - i); + *EVAL(list2 + (last - list1)) = list2 + n * size; + +#ifdef NATURAL + p2 = list2; + f1 = list1; + sense = (cmp(f1, f1 + size TSRMLS_CC) > 0); + for (; f1 < last; sense = !sense) { + length = 2; + /* Find pairs with same sense. */ + for (f2 = f1 + size2; f2 < last; f2 += size2) { + if ((cmp(f2, f2+ size TSRMLS_CC) > 0) != sense) + break; + length += 2; + } + if (length < THRESHOLD) { /* Pairwise merge */ + do { + p2 = *EVAL(p2) = f1 + size2 - list1 + list2; + if (sense > 0) + swap (f1, f1 + size); + } while ((f1 += size2) < f2); + } else { /* Natural merge */ + l2 = f2; + for (f2 = f1 + size2; f2 < l2; f2 += size2) { + if ((cmp(f2-size, f2 TSRMLS_CC) > 0) != sense) { + p2 = *EVAL(p2) = f2 - list1 + list2; + if (sense > 0) + reverse(f1, f2-size); + f1 = f2; + } + } + if (sense > 0) + reverse (f1, f2-size); + f1 = f2; + if (f2 < last || cmp(f2 - size, f2 TSRMLS_CC) > 0) + p2 = *EVAL(p2) = f2 - list1 + list2; + else + p2 = *EVAL(p2) = list2 + n*size; + } + } +#else /* pairwise merge only. */ + for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { + p2 = *EVAL(p2) = p2 + size2; + if (cmp (f1, f1 + size TSRMLS_CC) > 0) + swap(f1, f1 + size); + } +#endif /* NATURAL */ +} +/* }}} */ + +/* {{{ insertionsort + * This is to avoid out-of-bounds addresses in sorting the + * last 4 elements. + */ +static void insertionsort(u_char *a, size_t n, size_t size, int (*cmp)(const void *, const void * TSRMLS_DC) TSRMLS_DC) +{ + u_char *ai, *s, *t, *u, tmp; + int i; + + for (ai = a+size; --n >= 1; ai += size) + for (t = ai; t > a; t -= size) { + u = t - size; + if (cmp(u, t TSRMLS_CC) <= 0) + break; + swap(u, t); + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ diff --git a/main/network.c b/main/network.c new file mode 100644 index 0000000..ba2ee1c --- /dev/null +++ b/main/network.c @@ -0,0 +1,1202 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Stig Venaas | + | Streams work by Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/*#define DEBUG_MAIN_NETWORK 1*/ + +#include "php.h" + +#include + +#ifdef PHP_WIN32 +# include "win32/inet.h" +# define O_RDONLY _O_RDONLY +# include "win32/param.h" +#elif defined(NETWARE) +#include +#include +#else +#include +#endif + +#include +#if HAVE_SYS_SOCKET_H +#include +#endif + +#ifndef _FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#if HAVE_SYS_POLL_H +#include +#endif + +#if defined(NETWARE) +#ifdef USE_WINSOCK +#include +#else +#include +#include +#include +#include +#include +#endif +#elif !defined(PHP_WIN32) +#include +#include +#if HAVE_ARPA_INET_H +#include +#endif +#endif + +#ifndef HAVE_INET_ATON +int inet_aton(const char *, struct in_addr *); +#endif + +#include "php_network.h" + +#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) +#undef AF_UNIX +#endif + +#if defined(AF_UNIX) +#include +#endif + +#include "ext/standard/file.h" + +#ifdef PHP_WIN32 +# include "win32/time.h" +# define SOCK_ERR INVALID_SOCKET +# define SOCK_CONN_ERR SOCKET_ERROR +# define PHP_TIMEOUT_ERROR_VALUE WSAETIMEDOUT + +#if HAVE_IPV6 +const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */ +#endif + +#else +# define SOCK_ERR -1 +# define SOCK_CONN_ERR -1 +# define PHP_TIMEOUT_ERROR_VALUE ETIMEDOUT +#endif + +#if HAVE_GETADDRINFO +#ifdef HAVE_GAI_STRERROR +# define PHP_GAI_STRERROR(x) (gai_strerror(x)) +#else +# define PHP_GAI_STRERROR(x) (php_gai_strerror(x)) +/* {{{ php_gai_strerror + */ +static const char *php_gai_strerror(int code) +{ + static struct { + int code; + const char *msg; + } values[] = { +# ifdef EAI_ADDRFAMILY + {EAI_ADDRFAMILY, "Address family for hostname not supported"}, +# endif + {EAI_AGAIN, "Temporary failure in name resolution"}, + {EAI_BADFLAGS, "Bad value for ai_flags"}, + {EAI_FAIL, "Non-recoverable failure in name resolution"}, + {EAI_FAMILY, "ai_family not supported"}, + {EAI_MEMORY, "Memory allocation failure"}, +# ifdef EAI_NODATA + {EAI_NODATA, "No address associated with hostname"}, +# endif + {EAI_NONAME, "Name or service not known"}, + {EAI_SERVICE, "Servname not supported for ai_socktype"}, + {EAI_SOCKTYPE, "ai_socktype not supported"}, + {EAI_SYSTEM, "System error"}, + {0, NULL} + }; + int i; + + for (i = 0; values[i].msg != NULL; i++) { + if (values[i].code == code) { + return (char *)values[i].msg; + } + } + + return "Unknown error"; +} +/* }}} */ +#endif +#endif + +/* {{{ php_network_freeaddresses + */ +PHPAPI void php_network_freeaddresses(struct sockaddr **sal) +{ + struct sockaddr **sap; + + if (sal == NULL) + return; + for (sap = sal; *sap != NULL; sap++) + efree(*sap); + efree(sal); +} +/* }}} */ + +/* {{{ php_network_getaddresses + * Returns number of addresses, 0 for none/error + */ +PHPAPI int php_network_getaddresses(const char *host, int socktype, struct sockaddr ***sal, char **error_string TSRMLS_DC) +{ + struct sockaddr **sap; + int n; +#if HAVE_GETADDRINFO +# if HAVE_IPV6 + static int ipv6_borked = -1; /* the way this is used *is* thread safe */ +# endif + struct addrinfo hints, *res, *sai; +#else + struct hostent *host_info; + struct in_addr in; +#endif + + if (host == NULL) { + return 0; + } +#if HAVE_GETADDRINFO + memset(&hints, '\0', sizeof(hints)); + + hints.ai_family = AF_INET; /* default to regular inet (see below) */ + hints.ai_socktype = socktype; + +# if HAVE_IPV6 + /* probe for a working IPv6 stack; even if detected as having v6 at compile + * time, at runtime some stacks are slow to resolve or have other issues + * if they are not correctly configured. + * static variable use is safe here since simple store or fetch operations + * are atomic and because the actual probe process is not in danger of + * collisions or race conditions. */ + if (ipv6_borked == -1) { + int s; + + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s == SOCK_ERR) { + ipv6_borked = 1; + } else { + ipv6_borked = 0; + closesocket(s); + } + } + hints.ai_family = ipv6_borked ? AF_INET : AF_UNSPEC; +# endif + + if ((n = getaddrinfo(host, NULL, &hints, &res))) { + if (error_string) { + spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed: %s", PHP_GAI_STRERROR(n)); + } + return 0; + } else if (res == NULL) { + if (error_string) { + spprintf(error_string, 0, "php_network_getaddresses: getaddrinfo failed (null result pointer) errno=%d", errno); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: getaddrinfo failed (null result pointer)"); + } + return 0; + } + + sai = res; + for (n = 1; (sai = sai->ai_next) != NULL; n++) + ; + + *sal = safe_emalloc((n + 1), sizeof(*sal), 0); + sai = res; + sap = *sal; + + do { + *sap = emalloc(sai->ai_addrlen); + memcpy(*sap, sai->ai_addr, sai->ai_addrlen); + sap++; + } while ((sai = sai->ai_next) != NULL); + + freeaddrinfo(res); +#else + if (!inet_aton(host, &in)) { + /* XXX NOT THREAD SAFE (is safe under win32) */ + host_info = gethostbyname(host); + if (host_info == NULL) { + if (error_string) { + spprintf(error_string, 0, "php_network_getaddresses: gethostbyname failed. errno=%d", errno); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", *error_string); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_network_getaddresses: gethostbyname failed"); + } + return 0; + } + in = *((struct in_addr *) host_info->h_addr); + } + + *sal = safe_emalloc(2, sizeof(*sal), 0); + sap = *sal; + *sap = emalloc(sizeof(struct sockaddr_in)); + (*sap)->sa_family = AF_INET; + ((struct sockaddr_in *)*sap)->sin_addr = in; + sap++; + n = 1; +#endif + + *sap = NULL; + return n; +} +/* }}} */ + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +#if !defined(__BEOS__) +# define HAVE_NON_BLOCKING_CONNECT 1 +# ifdef PHP_WIN32 +typedef u_long php_non_blocking_flags_t; +# define SET_SOCKET_BLOCKING_MODE(sock, save) \ + save = TRUE; ioctlsocket(sock, FIONBIO, &save) +# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \ + ioctlsocket(sock, FIONBIO, &save) +# else +typedef int php_non_blocking_flags_t; +# define SET_SOCKET_BLOCKING_MODE(sock, save) \ + save = fcntl(sock, F_GETFL, 0); \ + fcntl(sock, F_SETFL, save | O_NONBLOCK) +# define RESTORE_SOCKET_BLOCKING_MODE(sock, save) \ + fcntl(sock, F_SETFL, save) +# endif +#endif + +/* Connect to a socket using an interruptible connect with optional timeout. + * Optionally, the connect can be made asynchronously, which will implicitly + * enable non-blocking mode on the socket. + * */ +/* {{{ php_network_connect_socket */ +PHPAPI int php_network_connect_socket(php_socket_t sockfd, + const struct sockaddr *addr, + socklen_t addrlen, + int asynchronous, + struct timeval *timeout, + char **error_string, + int *error_code) +{ +#if HAVE_NON_BLOCKING_CONNECT + php_non_blocking_flags_t orig_flags; + int n; + int error = 0; + socklen_t len; + int ret = 0; + + SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags); + + if ((n = connect(sockfd, addr, addrlen)) != 0) { + error = php_socket_errno(); + + if (error_code) { + *error_code = error; + } + + if (error != EINPROGRESS) { + if (error_string) { + *error_string = php_socket_strerror(error, NULL, 0); + } + + return -1; + } + if (asynchronous && error == EINPROGRESS) { + /* this is fine by us */ + return 0; + } + } + + if (n == 0) { + goto ok; + } +# ifdef PHP_WIN32 + /* The documentation for connect() says in case of non-blocking connections + * the select function reports success in the writefds set and failure in + * the exceptfds set. Indeed, using PHP_POLLREADABLE results in select + * failing only due to the timeout and not immediately as would be + * expected when a connection is actively refused. This way, + * php_pollfd_for will return a mask with POLLOUT if the connection + * is successful and with POLLPRI otherwise. */ + if ((n = php_pollfd_for(sockfd, POLLOUT|POLLPRI, timeout)) == 0) { +#else + if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) { +#endif + error = PHP_TIMEOUT_ERROR_VALUE; + } + + if (n > 0) { + len = sizeof(error); + /* + BSD-derived systems set errno correctly + Solaris returns -1 from getsockopt in case of error + */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) { + ret = -1; + } + } else { + /* whoops: sockfd has disappeared */ + ret = -1; + } + +ok: + if (!asynchronous) { + /* back to blocking mode */ + RESTORE_SOCKET_BLOCKING_MODE(sockfd, orig_flags); + } + + if (error_code) { + *error_code = error; + } + + if (error) { + ret = -1; + if (error_string) { + *error_string = php_socket_strerror(error, NULL, 0); + } + } + return ret; +#else + if (asynchronous) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Asynchronous connect() not supported on this platform"); + } + return (connect(sockfd, addr, addrlen) == 0) ? 0 : -1; +#endif +} +/* }}} */ + +/* {{{ sub_times */ +static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result) +{ + result->tv_usec = a.tv_usec - b.tv_usec; + if (result->tv_usec < 0L) { + a.tv_sec--; + result->tv_usec += 1000000L; + } + result->tv_sec = a.tv_sec - b.tv_sec; + if (result->tv_sec < 0L) { + result->tv_sec++; + result->tv_usec -= 1000000L; + } +} +/* }}} */ + +/* Bind to a local IP address. + * Returns the bound socket, or -1 on failure. + * */ +/* {{{ php_network_bind_socket_to_local_addr */ +php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port, + int socktype, char **error_string, int *error_code + TSRMLS_DC) +{ + int num_addrs, n, err = 0; + php_socket_t sock; + struct sockaddr **sal, **psal, *sa; + socklen_t socklen; + + num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC); + + if (num_addrs == 0) { + /* could not resolve address(es) */ + return -1; + } + + for (sal = psal; *sal != NULL; sal++) { + sa = *sal; + + /* create a socket for this address */ + sock = socket(sa->sa_family, socktype, 0); + + if (sock == SOCK_ERR) { + continue; + } + + switch (sa->sa_family) { +#if HAVE_GETADDRINFO && HAVE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family; + ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); + socklen = sizeof(struct sockaddr_in6); + break; +#endif + case AF_INET: + ((struct sockaddr_in *)sa)->sin_family = sa->sa_family; + ((struct sockaddr_in *)sa)->sin_port = htons(port); + socklen = sizeof(struct sockaddr_in); + break; + default: + /* Unknown family */ + socklen = 0; + sa = NULL; + } + + if (sa) { + /* attempt to bind */ + +#ifdef SO_REUSEADDR + { + int val = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val)); + } +#endif + + n = bind(sock, sa, socklen); + + if (n != SOCK_CONN_ERR) { + goto bound; + } + + err = php_socket_errno(); + } + + closesocket(sock); + } + sock = -1; + + if (error_code) { + *error_code = err; + } + if (error_string) { + *error_string = php_socket_strerror(err, NULL, 0); + } + +bound: + + php_network_freeaddresses(psal); + + return sock; + +} +/* }}} */ + +PHPAPI int php_network_parse_network_address_with_port(const char *addr, long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC) +{ + char *colon; + char *tmp; + int ret = FAILURE; + short port; + struct sockaddr_in *in4 = (struct sockaddr_in*)sa; + struct sockaddr **psal; + int n; + char *errstr = NULL; +#if HAVE_IPV6 + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa; +#endif + + if (*addr == '[') { + colon = memchr(addr + 1, ']', addrlen-1); + if (!colon || colon[1] != ':') { + return FAILURE; + } + port = atoi(colon + 2); + addr++; + } else { + colon = memchr(addr, ':', addrlen); + if (!colon) { + return FAILURE; + } + port = atoi(colon + 1); + } + + tmp = estrndup(addr, colon - addr); + + /* first, try interpreting the address as a numeric address */ + +#if HAVE_IPV6 && HAVE_INET_PTON + if (inet_pton(AF_INET6, tmp, &in6->sin6_addr) > 0) { + in6->sin6_port = htons(port); + in6->sin6_family = AF_INET6; + *sl = sizeof(struct sockaddr_in6); + ret = SUCCESS; + goto out; + } +#endif + if (inet_aton(tmp, &in4->sin_addr) > 0) { + in4->sin_port = htons(port); + in4->sin_family = AF_INET; + *sl = sizeof(struct sockaddr_in); + ret = SUCCESS; + goto out; + } + + /* looks like we'll need to resolve it */ + n = php_network_getaddresses(tmp, SOCK_DGRAM, &psal, &errstr TSRMLS_CC); + + if (n == 0) { + if (errstr) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to resolve `%s': %s", tmp, errstr); + STR_FREE(errstr); + } + goto out; + } + + /* copy the details from the first item */ + switch ((*psal)->sa_family) { +#if HAVE_GETADDRINFO && HAVE_IPV6 + case AF_INET6: + *in6 = **(struct sockaddr_in6**)psal; + in6->sin6_port = htons(port); + *sl = sizeof(struct sockaddr_in6); + ret = SUCCESS; + break; +#endif + case AF_INET: + *in4 = **(struct sockaddr_in**)psal; + in4->sin_port = htons(port); + *sl = sizeof(struct sockaddr_in); + ret = SUCCESS; + break; + } + + php_network_freeaddresses(psal); + +out: + STR_FREE(tmp); + return ret; +} + + +PHPAPI void php_network_populate_name_from_sockaddr( + /* input address */ + struct sockaddr *sa, socklen_t sl, + /* output readable address */ + char **textaddr, long *textaddrlen, + /* output address */ + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC) +{ + if (addr) { + *addr = emalloc(sl); + memcpy(*addr, sa, sl); + *addrlen = sl; + } + + if (textaddr) { +#if HAVE_IPV6 && HAVE_INET_NTOP + char abuf[256]; +#endif + char *buf = NULL; + + switch (sa->sa_family) { + case AF_INET: + /* generally not thread safe, but it *is* thread safe under win32 */ + buf = inet_ntoa(((struct sockaddr_in*)sa)->sin_addr); + if (buf) { + *textaddrlen = spprintf(textaddr, 0, "%s:%d", + buf, ntohs(((struct sockaddr_in*)sa)->sin_port)); + } + + break; + +#if HAVE_IPV6 && HAVE_INET_NTOP + case AF_INET6: + buf = (char*)inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, (char *)&abuf, sizeof(abuf)); + if (buf) { + *textaddrlen = spprintf(textaddr, 0, "%s:%d", + buf, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); + } + + break; +#endif +#ifdef AF_UNIX + case AF_UNIX: + { + struct sockaddr_un *ua = (struct sockaddr_un*)sa; + + if (ua->sun_path[0] == '\0') { + /* abstract name */ + int len = strlen(ua->sun_path + 1) + 1; + *textaddrlen = len; + *textaddr = emalloc(len + 1); + memcpy(*textaddr, ua->sun_path, len); + (*textaddr)[len] = '\0'; + } else { + *textaddrlen = strlen(ua->sun_path); + *textaddr = estrndup(ua->sun_path, *textaddrlen); + } + } + break; +#endif + + } + + } +} + +PHPAPI int php_network_get_peer_name(php_socket_t sock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC) +{ + php_sockaddr_storage sa; + socklen_t sl = sizeof(sa); + memset(&sa, 0, sizeof(sa)); + + if (getpeername(sock, (struct sockaddr*)&sa, &sl) == 0) { + php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl, + textaddr, textaddrlen, + addr, addrlen + TSRMLS_CC); + return 0; + } + return -1; +} + +PHPAPI int php_network_get_sock_name(php_socket_t sock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC) +{ + php_sockaddr_storage sa; + socklen_t sl = sizeof(sa); + memset(&sa, 0, sizeof(sa)); + + if (getsockname(sock, (struct sockaddr*)&sa, &sl) == 0) { + php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl, + textaddr, textaddrlen, + addr, addrlen + TSRMLS_CC); + return 0; + } + return -1; + +} + + +/* Accept a client connection from a server socket, + * using an optional timeout. + * Returns the peer address in addr/addrlen (it will emalloc + * these, so be sure to efree the result). + * If you specify textaddr/textaddrlen, a text-printable + * version of the address will be emalloc'd and returned. + * */ + +/* {{{ php_network_accept_incoming */ +PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen, + struct timeval *timeout, + char **error_string, + int *error_code + TSRMLS_DC) +{ + php_socket_t clisock = -1; + int error = 0, n; + php_sockaddr_storage sa; + socklen_t sl; + + n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout); + + if (n == 0) { + error = PHP_TIMEOUT_ERROR_VALUE; + } else if (n == -1) { + error = php_socket_errno(); + } else { + sl = sizeof(sa); + + clisock = accept(srvsock, (struct sockaddr*)&sa, &sl); + + if (clisock != SOCK_ERR) { + php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl, + textaddr, textaddrlen, + addr, addrlen + TSRMLS_CC); + } else { + error = php_socket_errno(); + } + } + + if (error_code) { + *error_code = error; + } + if (error_string) { + *error_string = php_socket_strerror(error, NULL, 0); + } + + return clisock; +} +/* }}} */ + + + +/* Connect to a remote host using an interruptible connect with optional timeout. + * Optionally, the connect can be made asynchronously, which will implicitly + * enable non-blocking mode on the socket. + * Returns the connected (or connecting) socket, or -1 on failure. + * */ + +/* {{{ php_network_connect_socket_to_host */ +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 + TSRMLS_DC) +{ + int num_addrs, n, fatal = 0; + php_socket_t sock; + struct sockaddr **sal, **psal, *sa; + struct timeval working_timeout; + socklen_t socklen; +#if HAVE_GETTIMEOFDAY + struct timeval limit_time, time_now; +#endif + + num_addrs = php_network_getaddresses(host, socktype, &psal, error_string TSRMLS_CC); + + if (num_addrs == 0) { + /* could not resolve address(es) */ + return -1; + } + + if (timeout) { + memcpy(&working_timeout, timeout, sizeof(working_timeout)); +#if HAVE_GETTIMEOFDAY + gettimeofday(&limit_time, NULL); + limit_time.tv_sec += working_timeout.tv_sec; + limit_time.tv_usec += working_timeout.tv_usec; + if (limit_time.tv_usec >= 1000000) { + limit_time.tv_usec -= 1000000; + limit_time.tv_sec++; + } +#endif + } + + for (sal = psal; !fatal && *sal != NULL; sal++) { + sa = *sal; + + /* create a socket for this address */ + sock = socket(sa->sa_family, socktype, 0); + + if (sock == SOCK_ERR) { + continue; + } + + switch (sa->sa_family) { +#if HAVE_GETADDRINFO && HAVE_IPV6 + case AF_INET6: + if (!bindto || strchr(bindto, ':')) { + ((struct sockaddr_in6 *)sa)->sin6_family = sa->sa_family; + ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); + socklen = sizeof(struct sockaddr_in6); + } else { + socklen = 0; + sa = NULL; + } + break; +#endif + case AF_INET: + ((struct sockaddr_in *)sa)->sin_family = sa->sa_family; + ((struct sockaddr_in *)sa)->sin_port = htons(port); + socklen = sizeof(struct sockaddr_in); + break; + default: + /* Unknown family */ + socklen = 0; + sa = NULL; + } + + if (sa) { + /* make a connection attempt */ + + if (bindto) { + struct sockaddr *local_address = NULL; + int local_address_len = 0; + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *in4 = emalloc(sizeof(struct sockaddr_in)); + + local_address = (struct sockaddr*)in4; + local_address_len = sizeof(struct sockaddr_in); + + in4->sin_family = sa->sa_family; + in4->sin_port = htons(bindport); + if (!inet_aton(bindto, &in4->sin_addr)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto); + goto skip_bind; + } + memset(&(in4->sin_zero), 0, sizeof(in4->sin_zero)); + } +#if HAVE_IPV6 && HAVE_INET_PTON + else { /* IPV6 */ + struct sockaddr_in6 *in6 = emalloc(sizeof(struct sockaddr_in6)); + + local_address = (struct sockaddr*)in6; + local_address_len = sizeof(struct sockaddr_in6); + + in6->sin6_family = sa->sa_family; + in6->sin6_port = htons(bindport); + if (inet_pton(AF_INET6, bindto, &in6->sin6_addr) < 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid IP Address: %s", bindto); + goto skip_bind; + } + } +#endif + if (!local_address || bind(sock, local_address, local_address_len)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to bind to '%s:%d', system said: %s", bindto, bindport, strerror(errno)); + } +skip_bind: + if (local_address) { + efree(local_address); + } + } + /* free error string recieved during previous iteration (if any) */ + if (error_string && *error_string) { + efree(*error_string); + *error_string = NULL; + } + + n = php_network_connect_socket(sock, sa, socklen, asynchronous, + timeout ? &working_timeout : NULL, + error_string, error_code); + + if (n != -1) { + goto connected; + } + + /* adjust timeout for next attempt */ +#if HAVE_GETTIMEOFDAY + if (timeout) { + gettimeofday(&time_now, NULL); + + if (timercmp(&time_now, &limit_time, >=)) { + /* time limit expired; don't attempt any further connections */ + fatal = 1; + } else { + /* work out remaining time */ + sub_times(limit_time, time_now, &working_timeout); + } + } +#else + if (error_code && *error_code == PHP_TIMEOUT_ERROR_VALUE) { + /* Don't even bother trying to connect to the next alternative; + * we have no way to determine how long we have already taken + * and it is quite likely that the next attempt will fail too. */ + fatal = 1; + } else { + /* re-use the same initial timeout. + * Not the best thing, but in practice it should be good-enough */ + if (timeout) { + memcpy(&working_timeout, timeout, sizeof(working_timeout)); + } + } +#endif + } + + closesocket(sock); + } + sock = -1; + +connected: + + php_network_freeaddresses(psal); + + return sock; +} +/* }}} */ + +/* {{{ php_any_addr + * Fills the any (wildcard) address into php_sockaddr_storage + */ +PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port) +{ + memset(addr, 0, sizeof(php_sockaddr_storage)); + switch (family) { +#if HAVE_IPV6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + sin6->sin6_addr = in6addr_any; + break; + } +#endif + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *) addr; + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_addr.s_addr = htonl(INADDR_ANY); + break; + } + } +} +/* }}} */ + +/* {{{ php_sockaddr_size + * Returns the size of struct sockaddr_xx for the family + */ +PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr) +{ + switch (((struct sockaddr *)addr)->sa_family) { + case AF_INET: + return sizeof(struct sockaddr_in); +#if HAVE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6); +#endif +#ifdef AF_UNIX + case AF_UNIX: + return sizeof(struct sockaddr_un); +#endif + default: + return 0; + } +} +/* }}} */ + +/* Given a socket error code, if buf == NULL: + * emallocs storage for the error message and returns + * else + * sprintf message into provided buffer and returns buf + */ +/* {{{ php_socket_strerror */ +PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) +{ +#ifndef PHP_WIN32 + char *errstr; + + errstr = strerror(err); + if (buf == NULL) { + buf = estrdup(errstr); + } else { + strncpy(buf, errstr, bufsize); + } + return buf; +#else + char *sysbuf; + int free_it = 1; + + if (!FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&sysbuf, + 0, + NULL)) { + free_it = 0; + sysbuf = "Unknown Error"; + } + + if (buf == NULL) { + buf = estrdup(sysbuf); + } else { + strncpy(buf, sysbuf, bufsize); + } + + if (free_it) { + LocalFree(sysbuf); + } + + return buf; +#endif +} +/* }}} */ + +/* deprecated */ +PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC TSRMLS_DC) +{ + php_stream *stream; + php_netstream_data_t *sock; + + sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0); + memset(sock, 0, sizeof(php_netstream_data_t)); + + sock->is_blocked = 1; + sock->timeout.tv_sec = FG(default_socket_timeout); + sock->timeout.tv_usec = 0; + sock->socket = socket; + + stream = php_stream_alloc_rel(&php_stream_generic_socket_ops, sock, persistent_id, "r+"); + + if (stream == NULL) { + pefree(sock, persistent_id ? 1 : 0); + } else { + stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING; + } + + return stream; +} + +PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port, + int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC) +{ + char *res; + long reslen; + php_stream *stream; + + reslen = spprintf(&res, 0, "tcp://%s:%d", host, port); + + stream = php_stream_xport_create(res, reslen, REPORT_ERRORS, + STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id, timeout, NULL, NULL, NULL); + + efree(res); + + return stream; +} + +PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC) +{ + int ret = SUCCESS; + int flags; + int myflag = 0; + +#ifdef PHP_WIN32 + /* with ioctlsocket, a non-zero sets nonblocking, a zero sets blocking */ + flags = !block; + if (ioctlsocket(socketd, FIONBIO, &flags) == SOCKET_ERROR) { + ret = FAILURE; + } +#else + flags = fcntl(socketd, F_GETFL); +#ifdef O_NONBLOCK + myflag = O_NONBLOCK; /* POSIX version */ +#elif defined(O_NDELAY) + myflag = O_NDELAY; /* old non-POSIX version */ +#endif + if (!block) { + flags |= myflag; + } else { + flags &= ~myflag; + } + if (fcntl(socketd, F_SETFL, flags) == -1) { + ret = FAILURE; + } +#endif + return ret; +} + +PHPAPI void _php_emit_fd_setsize_warning(int max_fd) +{ + TSRMLS_FETCH(); + +#ifdef PHP_WIN32 + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n" + "If this binary is from an official www.php.net package, file a bug report\n" + "at http://bugs.php.net, including the following information:\n" + "FD_SETSIZE=%d, but you are using %d.\n" + " --enable-fd-setsize=%d is recommended, but you may want to set it\n" + "to match to maximum number of sockets each script will work with at\n" + "one time, in order to avoid seeing this error again at a later date.", + FD_SETSIZE, max_fd, (max_fd + 128) & ~127); +#else + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "You MUST recompile PHP with a larger value of FD_SETSIZE.\n" + "It is set to %d, but you have descriptors numbered at least as high as %d.\n" + " --enable-fd-setsize=%d is recommended, but you may want to set it\n" + "to equal the maximum number of open files supported by your system,\n" + "in order to avoid seeing this error again at a later date.", + FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023); +#endif +} + +#if defined(PHP_USE_POLL_2_EMULATION) + +/* emulate poll(2) using select(2), safely. */ + +PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout) +{ + fd_set rset, wset, eset; + php_socket_t max_fd = SOCK_ERR; + unsigned int i; + int n; + struct timeval tv; + + /* check the highest numbered descriptor */ + for (i = 0; i < nfds; i++) { + if (ufds[i].fd > max_fd) + max_fd = ufds[i].fd; + } + + PHP_SAFE_MAX_FD(max_fd, nfds + 1); + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_ZERO(&eset); + + for (i = 0; i < nfds; i++) { + if (ufds[i].events & PHP_POLLREADABLE) { + PHP_SAFE_FD_SET(ufds[i].fd, &rset); + } + if (ufds[i].events & POLLOUT) { + PHP_SAFE_FD_SET(ufds[i].fd, &wset); + } + if (ufds[i].events & POLLPRI) { + PHP_SAFE_FD_SET(ufds[i].fd, &eset); + } + } + + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; + } +/* Reseting/initializing */ +#ifdef PHP_WIN32 + WSASetLastError(0); +#else + errno = 0; +#endif + n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL); + + if (n >= 0) { + for (i = 0; i < nfds; i++) { + ufds[i].revents = 0; + + if (PHP_SAFE_FD_ISSET(ufds[i].fd, &rset)) { + /* could be POLLERR or POLLHUP but can't tell without probing */ + ufds[i].revents |= POLLIN; + } + if (PHP_SAFE_FD_ISSET(ufds[i].fd, &wset)) { + ufds[i].revents |= POLLOUT; + } + if (PHP_SAFE_FD_ISSET(ufds[i].fd, &eset)) { + ufds[i].revents |= POLLPRI; + } + } + } + return n; +} + +#endif + + +/* + * Local variables: + * tab-width: 8 + * c-basic-offset: 8 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/main/output.c b/main/output.c new file mode 100644 index 0000000..004b066 --- /dev/null +++ b/main/output.c @@ -0,0 +1,1578 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Zeev Suraski | + | Thies C. Arntzen | + | Marcus Boerger | + | New API: Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $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/url_scanner_ex.h" +#include "SAPI.h" +#include "zend_stack.h" +#include "php_output.h" + +ZEND_DECLARE_MODULE_GLOBALS(output); + +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"; + +#if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG +# undef inline +# define inline +#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); + +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 */ +#ifdef PHP_WIN32 + fflush(stderr); +#endif + return str_len; +} +static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr; +/* }}} */ + +/* {{{ void php_output_header(TSRMLS_D) */ +static void php_output_header(TSRMLS_D) +{ + 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; + } + } +} +/* }}} */ + +/* {{{ 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 + memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals)); +#else + memset(&output_globals, 0, sizeof(zend_output_globals)); +#endif + + zend_stack_init(&OG(handlers)); + OG(flags) |= PHP_OUTPUT_ACTIVATED; + + return SUCCESS; +} +/* }}} */ + +/* {{{ void php_output_deactivate(TSRMLS_D) + * Destroy the output handler stack */ +PHPAPI void php_output_deactivate(TSRMLS_D) +{ + 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)); + } + +} +/* }}} */ + +/* {{{ 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_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); +} +/* }}} */ + +/* {{{ 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(flags) = (OG(flags) & ~0xf) | (status & 0xf); +} +/* }}} */ + +/* {{{ int php_output_get_status(TSRMLS_C) + * Get output control status */ +PHPAPI int php_output_get_status(TSRMLS_D) +{ + return ( + OG(flags) + | (OG(active) ? PHP_OUTPUT_ACTIVE : 0) + | (OG(running)? PHP_OUTPUT_LOCKED : 0) + ) & 0xff; +} +/* }}} */ + +/* {{{ 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(flags) & PHP_OUTPUT_DISABLED) { + return 0; + } + if (OG(flags) & PHP_OUTPUT_ACTIVATED) { + return sapi_module.ub_write(str, len TSRMLS_CC); + } + return php_output_direct(str, len); +} +/* }}} */ + +/* {{{ 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) +{ + 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); +} +/* }}} */ + +/* {{{ 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 *)); + } + php_output_context_dtor(&context); + return SUCCESS; + } + 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); + } +} +/* }}} */ + +/* {{{ 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) +{ + php_output_context context; + + if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) { + 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; +} +/* }}} */ + +/* {{{ 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) +{ + php_output_context context; + + 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); + } +} + +/* {{{ 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; +} +/* }}} */ + +/* {{{ 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)); +} +/* }}} */ + +/* {{{ 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; +} +/* }}} */ + +/* {{{ 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); + } +} +/* }}} */ + +/* {{{ 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; +} +/* }}} */ + +/* {{{ 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; + } +} + +/* {{{ 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; + } +} +/* }}} */ + +/* {{{ 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); +} +/* }}} */ + +/* {{{ 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; + + 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; + } + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; +} +/* }}} */ + +/* {{{ 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; + + 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; +} +/* }}} */ + +/* {{{ 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 (output_handler) { + handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC); + } else { + handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC); + } + if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) { + return SUCCESS; + } + php_output_handler_free(&handler TSRMLS_CC); + return FAILURE; +} +/* }}} */ + +/* {{{ 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) +{ + 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_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) +{ + 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); + } + } + + return handler; +} +/* }}} */ + +/* {{{ 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) +{ + 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; +} +/* }}} */ + +/* {{{ 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) +{ + if (handler->dtor && handler->opaq) { + handler->dtor(handler->opaq TSRMLS_CC); + } + handler->dtor = dtor; + handler->opaq = opaq; +} +/* }}} */ + +/* {{{ 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) +{ + HashPosition pos; + HashTable *rconflicts; + php_output_handler_conflict_check_t *conflict; + + if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) { + return FAILURE; + } + 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; +} +/* }}} */ + +/* {{{ 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) +{ + php_output_handler ***handlers; + int i, count = php_output_get_level(TSRMLS_C); + + if (count) { + handlers = (php_output_handler ***) zend_stack_base(&OG(handlers)); + + for (i = 0; i < count; ++i) { + if (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) { + return 1; + } + } + } + + return 0; +} +/* }}} */ + +/* {{{ 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_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; +} +/* }}} */ + +/* {{{ 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) +{ + HashTable rev, *rev_ptr = NULL; + + if (!EG(current_module)) { + zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT"); + return FAILURE; + } + + 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; + } + if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) { + zend_hash_destroy(&rev); + return FAILURE; + } + return SUCCESS; + } +} +/* }}} */ + +/* {{{ 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) +{ + php_output_handler_alias_ctor_t *func = NULL; + + zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func); + return func; +} +/* }}} */ + +/* {{{ 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) +{ + 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 { + OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH; + } +} +/* }}} */ + +/* {{{ 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) +{ + return OG(output_start_filename); +} +/* }}} */ + +/* {{{ 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) +{ + 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; +} +/* }}} */ + +/* {{{ 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) +{ + if (!context) { + context = emalloc(sizeof(php_output_context)); + } + + 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; + } + 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 1; +} +/* }}} */ + +/* {{{ 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) +{ + 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 + + 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; + } + + 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; + } + + 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); + } + + } 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; +} +/* }}} */ + + +/* {{{ 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) +{ + php_output_context context; + php_output_handler **active; + int obh_cnt; + + if (php_output_lock_error(op TSRMLS_CC)) { + return; + } + + php_output_context_init(&context, op TSRMLS_CC); + + /* + * 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; + + 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; + } + + 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); + + if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) { + sapi_flush(TSRMLS_C); + } + + OG(flags) |= PHP_OUTPUT_SENT; + } + } + php_output_context_dtor(&context); +} +/* }}} */ + +/* {{{ 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) +{ + 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; + } +} +/* }}} */ + +/* {{{ 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; +} +/* }}} */ + +/* {{{ 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_output_handler *handler = *(php_output_handler **) h; + zval *array = (zval *) z; + + add_next_index_stringl(array, handler->name, handler->name_len, 1); + return 0; +} +/* }}} */ + +/* {{{ 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) +{ + php_output_handler *handler = *(php_output_handler **) h; + zval *array = (zval *) z; + + add_next_index_zval(array, php_output_handler_status(handler, NULL)); + + return 0; +} + +/* {{{ 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); + } + + 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; +} +/* }}} */ + +/* {{{ 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) +{ + php_output_context context; + php_output_handler **current, *orphan = OG(active); + + 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; + } + 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; + } + + /* 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); + } + + /* destroy the handler (after write!) */ + php_output_handler_free(&orphan TSRMLS_CC); + php_output_context_dtor(&context); + + return 1; + } +} +/* }}} */ + +/* {{{ 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) +{ + php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context; + PHP_OUTPUT_TSRMLS(output_context); + + if (func) { + char *out_str = NULL; + uint out_len = 0; + + 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; + } + 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; +} +/* }}} */ + +/* + * USERLAND (nearly 1:1 of old output.c) + */ + +/* {{{ 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; + long flags = PHP_OUTPUT_HANDLER_STDFLAGS; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) { + return; + } + + if (chunk_size < 0) { + chunk_size = 0; + } + + 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; +} +/* }}} */ + +/* {{{ proto bool ob_flush(void) + Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */ +PHP_FUNCTION(ob_flush) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (!OG(active)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush"); + RETURN_FALSE; + } + + 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; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ob_clean(void) + Clean (delete) the current output buffer */ +PHP_FUNCTION(ob_clean) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (!OG(active)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); + RETURN_FALSE; + } + + 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; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool ob_end_flush(void) + Flush (send) the output buffer, and delete current output buffer */ +PHP_FUNCTION(ob_end_flush) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + 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; + } + + RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C)); +} +/* }}} */ + +/* {{{ proto bool ob_end_clean(void) + Clean the output buffer, and delete current output buffer */ +PHP_FUNCTION(ob_end_clean) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (!OG(active)) { + php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete"); + RETURN_FALSE; + } + + RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C)); +} +/* }}} */ + +/* {{{ proto bool ob_get_flush(void) + Get current buffer contents, flush (send) the output buffer, and delete current output buffer */ +PHP_FUNCTION(ob_get_flush) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + 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"); + 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); + } +} +/* }}} */ + +/* {{{ proto bool ob_get_clean(void) + Get current buffer contents and delete current output buffer */ +PHP_FUNCTION(ob_get_clean) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if(!OG(active)) { + RETURN_FALSE; + } + + 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"); + RETURN_FALSE; + } + + 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); + } +} +/* }}} */ + +/* {{{ proto string ob_get_contents(void) + Return the contents of the output buffer */ +PHP_FUNCTION(ob_get_contents) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto int ob_get_level(void) + Return the nesting level of the output buffer */ +PHP_FUNCTION(ob_get_level) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + RETURN_LONG(php_output_get_level(TSRMLS_C)); +} +/* }}} */ + +/* {{{ proto int ob_get_length(void) + Return the length of the output buffer */ +PHP_FUNCTION(ob_get_length) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto false|array ob_list_handlers() + List all output_buffers in an array */ +PHP_FUNCTION(ob_list_handlers) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + array_init(return_value); + + if (!OG(active)) { + return; + } + + zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value); +} +/* }}} */ + +/* {{{ proto false|array ob_get_status([bool full_status]) + Return the status of the active or all output buffers */ +PHP_FUNCTION(ob_get_status) +{ + zend_bool full_status = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) { + return; + } + + array_init(return_value); + + if (!OG(active)) { + return; + } + + if (full_status) { + 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); + } +} +/* }}} */ + +/* {{{ proto void ob_implicit_flush([int flag]) + Turn implicit flush on/off and is equivalent to calling flush() after every output call */ +PHP_FUNCTION(ob_implicit_flush) +{ + long flag = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) { + return; + } + + php_output_set_implicit_flush(flag TSRMLS_CC); +} +/* }}} */ + +/* {{{ proto bool output_reset_rewrite_vars(void) + Reset(clear) URL rewriter values */ +PHP_FUNCTION(output_reset_rewrite_vars) +{ + if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* {{{ proto bool output_add_rewrite_var(string name, string value) + Add URL rewriter values */ +PHP_FUNCTION(output_add_rewrite_var) +{ + char *name, *value; + int name_len, value_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ + +/* + * 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.h b/main/php.h new file mode 100644 index 0000000..9a7b092 --- /dev/null +++ b/main/php.h @@ -0,0 +1,457 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_H +#define PHP_H + +#ifdef HAVE_DMALLOC +#include +#endif + +#define PHP_API_VERSION 20100412 +#define PHP_HAVE_STREAMS +#define YYDEBUG 0 + +#include "php_version.h" +#include "zend.h" +#include "zend_qsort.h" +#include "php_compat.h" + +#include "zend_API.h" + +#undef sprintf +#define sprintf php_sprintf + +/* PHP's DEBUG value must match Zend's ZEND_DEBUG value */ +#undef PHP_DEBUG +#define PHP_DEBUG ZEND_DEBUG + +#ifdef PHP_WIN32 +# include "tsrm_win32.h" +# include "win95nt.h" +# ifdef PHP_EXPORTS +# define PHPAPI __declspec(dllexport) +# else +# define PHPAPI __declspec(dllimport) +# endif +# define PHP_DIR_SEPARATOR '\\' +# define PHP_EOL "\r\n" +#else +# if defined(__GNUC__) && __GNUC__ >= 4 +# define PHPAPI __attribute__ ((visibility("default"))) +# else +# define PHPAPI +# endif + +# define THREAD_LS +# define PHP_DIR_SEPARATOR '/' +# define PHP_EOL "\n" +#endif + +#ifdef NETWARE +/* For php_get_uname() function */ +#define PHP_UNAME "NetWare" +#define PHP_OS PHP_UNAME +#endif + +#if HAVE_ASSERT_H +#if PHP_DEBUG +#undef NDEBUG +#else +#ifndef NDEBUG +#define NDEBUG +#endif +#endif +#include +#else /* HAVE_ASSERT_H */ +#define assert(expr) ((void) (0)) +#endif /* HAVE_ASSERT_H */ + +#define APACHE 0 + +#if HAVE_UNIX_H +#include +#endif + +#if HAVE_ALLOCA_H +#include +#endif + +#if HAVE_BUILD_DEFS_H +#include +#endif + +/* + * This is a fast version of strlcpy which should be used, if you + * know the size of the destination buffer and if you know + * the length of the source string. + * + * size is the allocated number of bytes of dst + * src_size is the number of bytes excluding the NUL of src + */ + +#define PHP_STRLCPY(dst, src, size, src_size) \ + { \ + size_t php_str_len; \ + \ + if (src_size >= size) \ + php_str_len = size - 1; \ + else \ + php_str_len = src_size; \ + memcpy(dst, src, php_str_len); \ + dst[php_str_len] = '\0'; \ + } + +#ifndef HAVE_STRLCPY +BEGIN_EXTERN_C() +PHPAPI size_t php_strlcpy(char *dst, const char *src, size_t siz); +END_EXTERN_C() +#undef strlcpy +#define strlcpy php_strlcpy +#endif + +#ifndef HAVE_STRLCAT +BEGIN_EXTERN_C() +PHPAPI size_t php_strlcat(char *dst, const char *src, size_t siz); +END_EXTERN_C() +#undef strlcat +#define strlcat php_strlcat +#endif + +#ifndef HAVE_STRTOK_R +BEGIN_EXTERN_C() +char *strtok_r(char *s, const char *delim, char **last); +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) +#define SET_MUTEX(a) +#define FREE_MUTEX(a) + +/* + * Then the ODBC support can use both iodbc and Solid, + * uncomment this. + * #define HAVE_ODBC (HAVE_IODBC|HAVE_SOLID) + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_STDARG_H +#include +#else +# if HAVE_SYS_VARARGS_H +# include +# endif +#endif + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(ap1, ap2) __va_copy((ap1), (ap2)) +# else +# define va_copy(ap1, ap2) memcpy((&ap1), (&ap2), sizeof(va_list)) +# endif +#endif + +#include "zend_hash.h" +#include "zend_alloc.h" +#include "zend_stack.h" + +#if STDC_HEADERS +# include +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# endif +# ifndef HAVE_MEMMOVE +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#ifndef HAVE_STRERROR +char *strerror(int); +#endif + +#if HAVE_PWD_H +# ifdef PHP_WIN32 +#include "win32/param.h" +# else +#include +#include +# endif +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647L +#endif + +#ifndef LONG_MIN +#define LONG_MIN (- LONG_MAX - 1) +#endif + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +#ifndef INT_MIN +#define INT_MIN (- INT_MAX - 1) +#endif + +#define PHP_GCC_VERSION ZEND_GCC_VERSION +#define PHP_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_MALLOC +#define PHP_ATTRIBUTE_FORMAT ZEND_ATTRIBUTE_FORMAT + +BEGIN_EXTERN_C() +#include "snprintf.h" +END_EXTERN_C() +#include "spprintf.h" + +#define EXEC_INPUT_BUF 4096 + +#define PHP_MIME_TYPE "application/x-httpd-php" + +/* macros */ +#define STR_PRINT(str) ((str)?(str):"") + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# elif defined(MAX_PATH) +# define MAXPATHLEN MAX_PATH +# else +# define MAXPATHLEN 256 /* Should be safe for any weird systems that do not define it */ +# 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) +#define PHP_SLEEP_NON_VOID +#define php_sleep sleep +extern char **environ; +#endif /* !defined(PHP_WIN32) */ + +#ifdef PHP_PWRITE_64 +ssize_t pwrite(int, void *, size_t, off64_t); +#endif + +#ifdef PHP_PREAD_64 +ssize_t pread(int, void *, size_t, off64_t); +#endif + +BEGIN_EXTERN_C() +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); +END_EXTERN_C() + +#define php_error zend_error +#define error_handling_t zend_error_handling_t + +BEGIN_EXTERN_C() +static inline ZEND_ATTRIBUTE_DEPRECATED void php_set_error_handling(error_handling_t error_handling, zend_class_entry *exception_class TSRMLS_DC) +{ + zend_replace_error_handling(error_handling, exception_class, NULL TSRMLS_CC); +} +static inline ZEND_ATTRIBUTE_DEPRECATED void php_std_error_handling() {} + +PHPAPI void php_verror(const char *docref, const char *params, int type, const char *format, va_list args TSRMLS_DC) PHP_ATTRIBUTE_FORMAT(printf, 4, 0); + +#ifdef ZTS +#define PHP_ATTR_FMT_OFFSET 1 +#else +#define PHP_ATTR_FMT_OFFSET 0 +#endif + +/* PHPAPI void php_error(int type, const char *format, ...); */ +PHPAPI void php_error_docref0(const char *docref TSRMLS_DC, int type, const char *format, ...) + PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 3, PHP_ATTR_FMT_OFFSET + 4); +PHPAPI void php_error_docref1(const char *docref TSRMLS_DC, const char *param1, int type, const char *format, ...) + PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 4, PHP_ATTR_FMT_OFFSET + 5); +PHPAPI void php_error_docref2(const char *docref TSRMLS_DC, const char *param1, const char *param2, int type, const char *format, ...) + PHP_ATTRIBUTE_FORMAT(printf, PHP_ATTR_FMT_OFFSET + 5, PHP_ATTR_FMT_OFFSET + 6); +#ifdef PHP_WIN32 +PHPAPI void php_win32_docref2_from_error(DWORD error, const char *param1, const char *param2 TSRMLS_DC); +#endif +END_EXTERN_C() + +#define php_error_docref php_error_docref0 + +#define zenderror phperror +#define zendlex phplex + +#define phpparse zendparse +#define phprestart zendrestart +#define phpin zendin + +#define php_memnstr zend_memnstr + +/* functions */ +BEGIN_EXTERN_C() +PHPAPI extern int (*php_register_internal_extensions_func)(TSRMLS_D); +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 */ +#define PHP_FN ZEND_FN +#define PHP_MN ZEND_MN +#define PHP_NAMED_FUNCTION ZEND_NAMED_FUNCTION +#define PHP_FUNCTION ZEND_FUNCTION +#define PHP_METHOD ZEND_METHOD + +#define PHP_RAW_NAMED_FE ZEND_RAW_NAMED_FE +#define PHP_NAMED_FE ZEND_NAMED_FE +#define PHP_FE ZEND_FE +#define PHP_DEP_FE ZEND_DEP_FE +#define PHP_FALIAS ZEND_FALIAS +#define PHP_DEP_FALIAS ZEND_DEP_FALIAS +#define PHP_ME ZEND_ME +#define PHP_MALIAS ZEND_MALIAS +#define PHP_ABSTRACT_ME ZEND_ABSTRACT_ME +#define PHP_ME_MAPPING ZEND_ME_MAPPING +#define PHP_FE_END ZEND_FE_END + +#define PHP_MODULE_STARTUP_N ZEND_MODULE_STARTUP_N +#define PHP_MODULE_SHUTDOWN_N ZEND_MODULE_SHUTDOWN_N +#define PHP_MODULE_ACTIVATE_N ZEND_MODULE_ACTIVATE_N +#define PHP_MODULE_DEACTIVATE_N ZEND_MODULE_DEACTIVATE_N +#define PHP_MODULE_INFO_N ZEND_MODULE_INFO_N + +#define PHP_MODULE_STARTUP_D ZEND_MODULE_STARTUP_D +#define PHP_MODULE_SHUTDOWN_D ZEND_MODULE_SHUTDOWN_D +#define PHP_MODULE_ACTIVATE_D ZEND_MODULE_ACTIVATE_D +#define PHP_MODULE_DEACTIVATE_D ZEND_MODULE_DEACTIVATE_D +#define PHP_MODULE_INFO_D ZEND_MODULE_INFO_D + +/* Compatibility macros */ +#define PHP_MINIT ZEND_MODULE_STARTUP_N +#define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N +#define PHP_RINIT ZEND_MODULE_ACTIVATE_N +#define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N +#define PHP_MINFO ZEND_MODULE_INFO_N +#define PHP_GINIT ZEND_GINIT +#define PHP_GSHUTDOWN ZEND_GSHUTDOWN + +#define PHP_MINIT_FUNCTION ZEND_MODULE_STARTUP_D +#define PHP_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D +#define PHP_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D +#define PHP_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D +#define PHP_MINFO_FUNCTION ZEND_MODULE_INFO_D +#define PHP_GINIT_FUNCTION ZEND_GINIT_FUNCTION +#define PHP_GSHUTDOWN_FUNCTION ZEND_GSHUTDOWN_FUNCTION + +#define PHP_MODULE_GLOBALS ZEND_MODULE_GLOBALS + + +/* Output support */ +#include "main/php_output.h" + + +#include "php_streams.h" +#include "php_memory_streams.h" +#include "fopen_wrappers.h" + + +/* Virtual current working directory support */ +#include "tsrm_virtual_cwd.h" + +#include "zend_constants.h" + +/* connection status states */ +#define PHP_CONNECTION_NORMAL 0 +#define PHP_CONNECTION_ABORTED 1 +#define PHP_CONNECTION_TIMEOUT 2 + +#include "php_reentrancy.h" + +/* Finding offsets of elements within structures. + * Taken from the Apache code, which in turn, was taken from X code... + */ + +#ifndef XtOffset +#if defined(CRAY) || (defined(__arm) && !(defined(LINUX) || defined(__riscos__))) +#ifdef __STDC__ +#define XtOffset(p_type, field) _Offsetof(p_type, field) +#else +#ifdef CRAY2 +#define XtOffset(p_type, field) \ + (sizeof(int)*((unsigned int)&(((p_type)NULL)->field))) + +#else /* !CRAY2 */ + +#define XtOffset(p_type, field) ((unsigned int)&(((p_type)NULL)->field)) + +#endif /* !CRAY2 */ +#endif /* __STDC__ */ +#else /* ! (CRAY || __arm) */ + +#define XtOffset(p_type, field) \ + ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) + +#endif /* !CRAY */ +#endif /* ! XtOffset */ + +#ifndef XtOffsetOf +#ifdef offsetof +#define XtOffsetOf(s_type, field) offsetof(s_type, field) +#else +#define XtOffsetOf(s_type, field) XtOffset(s_type*, field) +#endif +#endif /* !XtOffsetOf */ + +#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/php_compat.h b/main/php_compat.h new file mode 100644 index 0000000..936dd52 --- /dev/null +++ b/main/php_compat.h @@ -0,0 +1,378 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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 PHP_COMPAT_H +#define PHP_COMPAT_H + +#ifdef PHP_WIN32 +#include "config.w32.h" +#else +#include +#endif + +#if defined(HAVE_BUNDLED_PCRE) || !defined(PHP_VERSION) +#define pcre_compile php_pcre_compile +#define pcre_compile2 php_pcre_compile2 +#define pcre_copy_substring php_pcre_copy_substring +#define pcre_exec php_pcre_exec +#define pcre_get_substring php_pcre_get_substring +#define pcre_get_substring_list php_pcre_get_substring_list +#define pcre_maketables php_pcre_maketables +#define pcre_study php_pcre_study +#define pcre_version php_pcre_version +#define pcre_fullinfo php_pcre_fullinfo +#define pcre_free php_pcre_free +#define pcre_malloc php_pcre_malloc +#define pcre_config php_pcre_config +#define pcre_copy_named_substring php_pcre_copy_named_substring +#define pcre_free_substring php_pcre_free_substring +#define pcre_free_substring_list php_pcre_free_substring_list +#define pcre_get_named_substring php_pcre_get_named_substring +#define pcre_get_stringnumber php_pcre_get_stringnumber +#define pcre_refcount php_pcre_refcount +#define _pcre_ord2utf8 php__pcre_ord2utf8 +#define _pcre_try_flipped php__pcre_try_flipped +#define _pcre_valid_utf8 php__pcre_valid_utf8 +#define _pcre_xclass php__pcre_xclass +#define pcre_callout php_pcre_callout +#define _pcre_OP_lengths php__pcre_OP_lengths +#define _pcre_utt_names php__pcre_utt_names +#define _pcre_default_tables php__pcre_default_tables +#define pcre_get_stringtable_entries php_pcre_get_stringtable_entries +#define _pcre_is_newline php__pcre_is_newline +#define pcre_stack_free php_pcre_stack_free +#define pcre_stack_malloc php_pcre_stack_malloc +#define _pcre_utf8_table1 php__pcre_utf8_table1 +#define _pcre_utf8_table1_size php__pcre_utf8_table1_size +#define _pcre_utf8_table2 php__pcre_utf8_table2 +#define _pcre_utf8_table3 php__pcre_utf8_table3 +#define _pcre_utf8_table4 php__pcre_utf8_table4 +#define _pcre_utt php__pcre_utt +#define _pcre_utt_size php__pcre_utt_size +#define _pcre_was_newline php__pcre_was_newline +#define _pcre_ucd_records php__pcre_ucd_records +#define _pcre_ucd_stage1 php__pcre_ucd_stage1 +#define _pcre_ucd_stage2 php__pcre_ucd_stage2 +#define _pcre_ucp_gentype php__pcre_ucp_gentype +#endif + +#define lookup php_lookup +#define hashTableInit php_hashTableInit +#define hashTableDestroy php_hashTableDestroy +#define hashTableIterInit php_hashTableIterInit +#define hashTableIterNext php_hashTableIterNext + +#if defined(HAVE_LIBXML) && (defined(HAVE_XML) || defined(HAVE_XMLRPC)) && !defined(HAVE_LIBEXPAT) +#define XML_DefaultCurrent php_XML_DefaultCurrent +#define XML_ErrorString php_XML_ErrorString +#define XML_ExpatVersion php_XML_ExpatVersion +#define XML_ExpatVersionInfo php_XML_ExpatVersionInfo +#define XML_ExternalEntityParserCreate php_XML_ExternalEntityParserCreate +#define XML_GetBase php_XML_GetBase +#define XML_GetBuffer php_XML_GetBuffer +#define XML_GetCurrentByteCount php_XML_GetCurrentByteCount +#define XML_GetCurrentByteIndex php_XML_GetCurrentByteIndex +#define XML_GetCurrentColumnNumber php_XML_GetCurrentColumnNumber +#define XML_GetCurrentLineNumber php_XML_GetCurrentLineNumber +#define XML_GetErrorCode php_XML_GetErrorCode +#define XML_GetIdAttributeIndex php_XML_GetIdAttributeIndex +#define XML_GetInputContext php_XML_GetInputContext +#define XML_GetSpecifiedAttributeCount php_XML_GetSpecifiedAttributeCount +#define XmlGetUtf16InternalEncodingNS php_XmlGetUtf16InternalEncodingNS +#define XmlGetUtf16InternalEncoding php_XmlGetUtf16InternalEncoding +#define XmlGetUtf8InternalEncodingNS php_XmlGetUtf8InternalEncodingNS +#define XmlGetUtf8InternalEncoding php_XmlGetUtf8InternalEncoding +#define XmlInitEncoding php_XmlInitEncoding +#define XmlInitEncodingNS php_XmlInitEncodingNS +#define XmlInitUnknownEncoding php_XmlInitUnknownEncoding +#define XmlInitUnknownEncodingNS php_XmlInitUnknownEncodingNS +#define XML_ParseBuffer php_XML_ParseBuffer +#define XML_Parse php_XML_Parse +#define XML_ParserCreate_MM php_XML_ParserCreate_MM +#define XML_ParserCreateNS php_XML_ParserCreateNS +#define XML_ParserCreate php_XML_ParserCreate +#define XML_ParserFree php_XML_ParserFree +#define XmlParseXmlDecl php_XmlParseXmlDecl +#define XmlParseXmlDeclNS php_XmlParseXmlDeclNS +#define XmlPrologStateInitExternalEntity php_XmlPrologStateInitExternalEntity +#define XmlPrologStateInit php_XmlPrologStateInit +#define XML_SetAttlistDeclHandler php_XML_SetAttlistDeclHandler +#define XML_SetBase php_XML_SetBase +#define XML_SetCdataSectionHandler php_XML_SetCdataSectionHandler +#define XML_SetCharacterDataHandler php_XML_SetCharacterDataHandler +#define XML_SetCommentHandler php_XML_SetCommentHandler +#define XML_SetDefaultHandlerExpand php_XML_SetDefaultHandlerExpand +#define XML_SetDefaultHandler php_XML_SetDefaultHandler +#define XML_SetDoctypeDeclHandler php_XML_SetDoctypeDeclHandler +#define XML_SetElementDeclHandler php_XML_SetElementDeclHandler +#define XML_SetElementHandler php_XML_SetElementHandler +#define XML_SetEncoding php_XML_SetEncoding +#define XML_SetEndCdataSectionHandler php_XML_SetEndCdataSectionHandler +#define XML_SetEndDoctypeDeclHandler php_XML_SetEndDoctypeDeclHandler +#define XML_SetEndElementHandler php_XML_SetEndElementHandler +#define XML_SetEndNamespaceDeclHandler php_XML_SetEndNamespaceDeclHandler +#define XML_SetEntityDeclHandler php_XML_SetEntityDeclHandler +#define XML_SetExternalEntityRefHandlerArg php_XML_SetExternalEntityRefHandlerArg +#define XML_SetExternalEntityRefHandler php_XML_SetExternalEntityRefHandler +#define XML_SetNamespaceDeclHandler php_XML_SetNamespaceDeclHandler +#define XML_SetNotationDeclHandler php_XML_SetNotationDeclHandler +#define XML_SetNotStandaloneHandler php_XML_SetNotStandaloneHandler +#define XML_SetParamEntityParsing php_XML_SetParamEntityParsing +#define XML_SetProcessingInstructionHandler php_XML_SetProcessingInstructionHandler +#define XML_SetReturnNSTriplet php_XML_SetReturnNSTriplet +#define XML_SetStartCdataSectionHandler php_XML_SetStartCdataSectionHandler +#define XML_SetStartDoctypeDeclHandler php_XML_SetStartDoctypeDeclHandler +#define XML_SetStartElementHandler php_XML_SetStartElementHandler +#define XML_SetStartNamespaceDeclHandler php_XML_SetStartNamespaceDeclHandler +#define XML_SetUnknownEncodingHandler php_XML_SetUnknownEncodingHandler +#define XML_SetUnparsedEntityDeclHandler php_XML_SetUnparsedEntityDeclHandler +#define XML_SetUserData php_XML_SetUserData +#define XML_SetXmlDeclHandler php_XML_SetXmlDeclHandler +#define XmlSizeOfUnknownEncoding php_XmlSizeOfUnknownEncoding +#define XML_UseParserAsHandlerArg php_XML_UseParserAsHandlerArg +#define XmlUtf16Encode php_XmlUtf16Encode +#define XmlUtf8Encode php_XmlUtf8Encode +#define XML_FreeContentModel php_XML_FreeContentModel +#define XML_MemMalloc php_XML_MemMalloc +#define XML_MemRealloc php_XML_MemRealloc +#define XML_MemFree php_XML_MemFree +#define XML_UseForeignDTD php_XML_UseForeignDTD +#define XML_GetFeatureList php_XML_GetFeatureList +#define XML_ParserReset php_XML_ParserReset + +#ifdef HAVE_GD_BUNDLED +#define any2eucjp php_gd_any2eucjp +#define createwbmp php_gd_createwbmp +#define empty_output_buffer php_gd_empty_output_buffer +#define fill_input_buffer php_gd_fill_input_buffer +#define freewbmp php_gd_freewbmp +#define gdAlphaBlend php_gd_gdAlphaBlend +#define gdCompareInt php_gd_gdCompareInt +#define gdCosT php_gd_gdCosT +#define gdCtxPrintf php_gd_gdCtxPrintf +#define gdDPExtractData php_gd_gdDPExtractData +#define gdFontGetGiant php_gd_gdFontGetGiant +#define gdFontGetLarge php_gd_gdFontGetLarge +#define gdFontGetMediumBold php_gd_gdFontGetMediumBold +#define gdFontGetSmall php_gd_gdFontGetSmall +#define gdFontGetTiny php_gd_gdFontGetTiny +#define gdFontGiant php_gd_gdFontGiant +#define gdFontGiantData php_gd_gdFontGiantData +#define gdFontGiantRep php_gd_gdFontGiantRep +#define gdFontLarge php_gd_gdFontLarge +#define gdFontLargeData php_gd_gdFontLargeData +#define gdFontLargeRep php_gd_gdFontLargeRep +#define gdFontMediumBold php_gd_gdFontMediumBold +#define gdFontMediumBoldData php_gd_gdFontMediumBoldData +#define gdFontMediumBoldRep php_gd_gdFontMediumBoldRep +#define gdFontSmall php_gd_gdFontSmall +#define gdFontSmallData php_gd_gdFontSmallData +#define gdFontSmallRep php_gd_gdFontSmallRep +#define gdFontTiny php_gd_gdFontTiny +#define gdFontTinyData php_gd_gdFontTinyData +#define gdFontTinyRep php_gd_gdFontTinyRep +#define gdGetBuf php_gd_gdGetBuf +#define gdGetByte php_gd_gdGetByte +#define gdGetC php_gd_gdGetC +#define _gdGetColors php_gd__gdGetColors +#define gd_getin php_gd_gd_getin +#define gdGetInt php_gd_gdGetInt +#define gdGetWord php_gd_gdGetWord +#define gdImageAABlend php_gd_gdImageAABlend +#define gdImageAALine php_gd_gdImageAALine +#define gdImageAlphaBlending php_gd_gdImageAlphaBlending +#define gdImageAntialias php_gd_gdImageAntialias +#define gdImageArc php_gd_gdImageArc +#define gdImageBrightness php_gd_gdImageBrightness +#define gdImageChar php_gd_gdImageChar +#define gdImageCharUp php_gd_gdImageCharUp +#define gdImageColor php_gd_gdImageColor +#define gdImageColorAllocate php_gd_gdImageColorAllocate +#define gdImageColorAllocateAlpha php_gd_gdImageColorAllocateAlpha +#define gdImageColorClosest php_gd_gdImageColorClosest +#define gdImageColorClosestAlpha php_gd_gdImageColorClosestAlpha +#define gdImageColorClosestHWB php_gd_gdImageColorClosestHWB +#define gdImageColorDeallocate php_gd_gdImageColorDeallocate +#define gdImageColorExact php_gd_gdImageColorExact +#define gdImageColorExactAlpha php_gd_gdImageColorExactAlpha +#define gdImageColorMatch php_gd_gdImageColorMatch +#define gdImageColorResolve php_gd_gdImageColorResolve +#define gdImageColorResolveAlpha php_gd_gdImageColorResolveAlpha +#define gdImageColorTransparent php_gd_gdImageColorTransparent +#define gdImageCompare php_gd_gdImageCompare +#define gdImageContrast php_gd_gdImageContrast +#define gdImageConvolution php_gd_gdImageConvolution +#define gdImageCopy php_gd_gdImageCopy +#define gdImageCopyMerge php_gd_gdImageCopyMerge +#define gdImageCopyMergeGray php_gd_gdImageCopyMergeGray +#define gdImageCopyResampled php_gd_gdImageCopyResampled +#define gdImageCopyResized php_gd_gdImageCopyResized +#define gdImageCreate php_gd_gdImageCreate +#define gdImageCreateFromGd php_gd_gdImageCreateFromGd +#define gdImageCreateFromGd2 php_gd_gdImageCreateFromGd2 +#define gdImageCreateFromGd2Ctx php_gd_gdImageCreateFromGd2Ctx +#define gdImageCreateFromGd2Part php_gd_gdImageCreateFromGd2Part +#define gdImageCreateFromGd2PartCtx php_gd_gdImageCreateFromGd2PartCtx +#define gdImageCreateFromGd2PartPtr php_gd_gdImageCreateFromGd2PartPtr +#define gdImageCreateFromGd2Ptr php_gd_gdImageCreateFromGd2Ptr +#define gdImageCreateFromGdCtx php_gd_gdImageCreateFromGdCtx +#define gdImageCreateFromGdPtr php_gd_gdImageCreateFromGdPtr +#define gdImageCreateFromGif php_gd_gdImageCreateFromGif +#define gdImageCreateFromGifCtx php_gd_gdImageCreateFromGifCtx +#define gdImageCreateFromGifSource php_gd_gdImageCreateFromGifSource +#define gdImageCreateFromJpeg php_gd_gdImageCreateFromJpeg +#define gdImageCreateFromJpegCtx php_gd_gdImageCreateFromJpegCtx +#define gdImageCreateFromJpegPtr php_gd_gdImageCreateFromJpegPtr +#define gdImageCreateFromPng php_gd_gdImageCreateFromPng +#define gdImageCreateFromPngCtx php_gd_gdImageCreateFromPngCtx +#define gdImageCreateFromPngPtr php_gd_gdImageCreateFromPngPtr +#define gdImageCreateFromPngSource php_gd_gdImageCreateFromPngSource +#define gdImageCreateFromWBMP php_gd_gdImageCreateFromWBMP +#define gdImageCreateFromWBMPCtx php_gd_gdImageCreateFromWBMPCtx +#define gdImageCreateFromWBMPPtr php_gd_gdImageCreateFromWBMPPtr +#define gdImageCreateFromXbm php_gd_gdImageCreateFromXbm +#define gdImageCreatePaletteFromTrueColor php_gd_gdImageCreatePaletteFromTrueColor +#define gdImageCreateTrueColor php_gd_gdImageCreateTrueColor +#define gdImageDashedLine php_gd_gdImageDashedLine +#define gdImageDestroy php_gd_gdImageDestroy +#define gdImageEdgeDetectQuick php_gd_gdImageEdgeDetectQuick +#define gdImageEllipse php_gd_gdImageEllipse +#define gdImageEmboss php_gd_gdImageEmboss +#define gdImageFill php_gd_gdImageFill +#define gdImageFilledArc php_gd_gdImageFilledArc +#define gdImageFilledEllipse php_gd_gdImageFilledEllipse +#define gdImageFilledPolygon php_gd_gdImageFilledPolygon +#define gdImageFilledRectangle php_gd_gdImageFilledRectangle +#define _gdImageFillTiled php_gd__gdImageFillTiled +#define gdImageFillToBorder php_gd_gdImageFillToBorder +#define gdImageGaussianBlur php_gd_gdImageGaussianBlur +#define gdImageGd php_gd_gdImageGd +#define gdImageGd2 php_gd_gdImageGd2 +#define gdImageGd2Ptr php_gd_gdImageGd2Ptr +#define gdImageGdPtr php_gd_gdImageGdPtr +#define gdImageGetClip php_gd_gdImageGetClip +#define gdImageGetPixel php_gd_gdImageGetPixel +#define gdImageGetTrueColorPixel php_gd_gdImageGetTrueColorPixel +#define gdImageGif php_gd_gdImageGif +#define gdImageGifCtx php_gd_gdImageGifCtx +#define gdImageGifPtr php_gd_gdImageGifPtr +#define gdImageGrayScale php_gd_gdImageGrayScale +#define gdImageInterlace php_gd_gdImageInterlace +#define gdImageJpeg php_gd_gdImageJpeg +#define gdImageJpegCtx php_gd_gdImageJpegCtx +#define gdImageJpegPtr php_gd_gdImageJpegPtr +#define gdImageLine php_gd_gdImageLine +#define gdImageMeanRemoval php_gd_gdImageMeanRemoval +#define gdImageNegate php_gd_gdImageNegate +#define gdImagePaletteCopy php_gd_gdImagePaletteCopy +#define gdImagePng php_gd_gdImagePng +#define gdImagePngCtx php_gd_gdImagePngCtx +#define gdImagePngCtxEx php_gd_gdImagePngCtxEx +#define gdImagePngEx php_gd_gdImagePngEx +#define gdImagePngPtr php_gd_gdImagePngPtr +#define gdImagePngPtrEx php_gd_gdImagePngPtrEx +#define gdImagePngToSink php_gd_gdImagePngToSink +#define gdImagePolygon php_gd_gdImagePolygon +#define gdImageRectangle php_gd_gdImageRectangle +#define gdImageRotate php_gd_gdImageRotate +#define gdImageRotate180 php_gd_gdImageRotate180 +#define gdImageRotate270 php_gd_gdImageRotate270 +#define gdImageRotate45 php_gd_gdImageRotate45 +#define gdImageRotate90 php_gd_gdImageRotate90 +#define gdImageSaveAlpha php_gd_gdImageSaveAlpha +#define gdImageSelectiveBlur php_gd_gdImageSelectiveBlur +#define gdImageSetAntiAliased php_gd_gdImageSetAntiAliased +#define gdImageSetAntiAliasedDontBlend php_gd_gdImageSetAntiAliasedDontBlend +#define gdImageSetBrush php_gd_gdImageSetBrush +#define gdImageSetClip php_gd_gdImageSetClip +#define gdImageSetPixel php_gd_gdImageSetPixel +#define gdImageSetStyle php_gd_gdImageSetStyle +#define gdImageSetThickness php_gd_gdImageSetThickness +#define gdImageSetTile php_gd_gdImageSetTile +#define gdImageSkewX php_gd_gdImageSkewX +#define gdImageSkewY php_gd_gdImageSkewY +#define gdImageSmooth php_gd_gdImageSmooth +#define gdImageString php_gd_gdImageString +#define gdImageString16 php_gd_gdImageString16 +#define gdImageStringFT php_gd_gdImageStringFT +#define gdImageStringFTEx php_gd_gdImageStringFTEx +#define gdImageStringTTF php_gd_gdImageStringTTF +#define gdImageStringUp php_gd_gdImageStringUp +#define gdImageStringUp16 php_gd_gdImageStringUp16 +#define gdImageTrueColorToPalette php_gd_gdImageTrueColorToPalette +#define gdImageWBMP php_gd_gdImageWBMP +#define gdImageWBMPCtx php_gd_gdImageWBMPCtx +#define gdImageWBMPPtr php_gd_gdImageWBMPPtr +#define gdImageXbmCtx php_gd_gdImageXbmCtx +#define gdNewDynamicCtx php_gd_gdNewDynamicCtx +#define gdNewDynamicCtxEx php_gd_gdNewDynamicCtxEx +#define gdNewFileCtx php_gd_gdNewFileCtx +#define gdNewSSCtx php_gd_gdNewSSCtx +#define gdPutBuf php_gd_gdPutBuf +#define gdPutC php_gd_gdPutC +#define _gdPutColors php_gd__gdPutColors +#define gdPutInt php_gd_gdPutInt +#define gd_putout php_gd_gd_putout +#define gdPutWord php_gd_gdPutWord +#define gdSeek php_gd_gdSeek +#define gdSinT php_gd_gdSinT +#define gd_strtok_r php_gd_gd_strtok_r +#define gdTell php_gd_gdTell +#define getmbi php_gd_getmbi +#define init_destination php_gd_init_destination +#define init_source php_gd_init_source +#define jpeg_gdIOCtx_dest php_gd_jpeg_gdIOCtx_dest +#define jpeg_gdIOCtx_src php_gd_jpeg_gdIOCtx_src +#define lsqrt php_gd_lsqrt +#define printwbmp php_gd_printwbmp +#define Putchar php_gd_Putchar +#define putmbi php_gd_putmbi +#define Putword php_gd_Putword +#define readwbmp php_gd_readwbmp +#define skipheader php_gd_skipheader +#define skip_input_data php_gd_skip_input_data +#define term_destination php_gd_term_destination +#define term_source php_gd_term_source +#define writewbmp php_gd_writewbmp +#define ZeroDataBlock php_gd_ZeroDataBlock +#define gdCacheCreate php_gd_gdCacheCreate +#define gdCacheDelete php_gd_gdCacheDelete +#define gdCacheGet php_gd_gdCacheGet +#define gdFontCacheSetup php_gd_gdFontCacheSetup +#define gdFontCacheShutdown php_gd_gdFontCacheShutdown +#define gdFreeFontCache php_gd_gdFreeFontCache +#endif /* HAVE_GD_BUNDLED */ + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +#define XML_DTD 1 + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS 1 +#endif + +#ifdef PHP_EXPORTS +#define PCRE_STATIC +#endif + +#endif diff --git a/main/php_config.h.in b/main/php_config.h.in new file mode 100644 index 0000000..a8f4e9d --- /dev/null +++ b/main/php_config.h.in @@ -0,0 +1,2480 @@ +/* main/php_config.h.in. Generated from configure.in by autoheader. */ + + +#if defined(__GNUC__) && __GNUC__ >= 4 +# define ZEND_API __attribute__ ((visibility("default"))) +# define ZEND_DLEXPORT __attribute__ ((visibility("default"))) +#else +# define ZEND_API +# define ZEND_DLEXPORT +#endif + +#define ZEND_DLIMPORT + +#undef uint +#undef ulong + +/* Define if you want to enable memory limit support */ +#define MEMORY_LIMIT 0 + + +/* */ +#undef AIX + +/* Whether to use native BeOS threads */ +#undef BETHREADS + +/* */ +#undef BUGGY_SNMPRINT_VALUE + +/* */ +#undef CDB_INCLUDE_FILE + +/* Define if system uses EBCDIC */ +#undef CHARSET_EBCDIC + +/* Whether to build bcmath as dynamic module */ +#undef COMPILE_DL_BCMATH + +/* Whether to build bz2 as dynamic module */ +#undef COMPILE_DL_BZ2 + +/* Whether to build calendar as dynamic module */ +#undef COMPILE_DL_CALENDAR + +/* Whether to build ctype as dynamic module */ +#undef COMPILE_DL_CTYPE + +/* Whether to build curl as dynamic module */ +#undef COMPILE_DL_CURL + +/* Whether to build date as dynamic module */ +#undef COMPILE_DL_DATE + +/* Whether to build dba as dynamic module */ +#undef COMPILE_DL_DBA + +/* Whether to build dom as dynamic module */ +#undef COMPILE_DL_DOM + +/* Whether to build enchant as dynamic module */ +#undef COMPILE_DL_ENCHANT + +/* Whether to build ereg as dynamic module */ +#undef COMPILE_DL_EREG + +/* Whether to build exif as dynamic module */ +#undef COMPILE_DL_EXIF + +/* Whether to build fileinfo as dynamic module */ +#undef COMPILE_DL_FILEINFO + +/* Whether to build filter as dynamic module */ +#undef COMPILE_DL_FILTER + +/* Whether to build ftp as dynamic module */ +#undef COMPILE_DL_FTP + +/* Whether to build gd as dynamic module */ +#undef COMPILE_DL_GD + +/* Whether to build gettext as dynamic module */ +#undef COMPILE_DL_GETTEXT + +/* Whether to build gmp as dynamic module */ +#undef COMPILE_DL_GMP + +/* Whether to build hash as dynamic module */ +#undef COMPILE_DL_HASH + +/* Whether to build iconv as dynamic module */ +#undef COMPILE_DL_ICONV + +/* Whether to build imap as dynamic module */ +#undef COMPILE_DL_IMAP + +/* Whether to build interbase as dynamic module */ +#undef COMPILE_DL_INTERBASE + +/* Whether to build intl as dynamic module */ +#undef COMPILE_DL_INTL + +/* Whether to build json as dynamic module */ +#undef COMPILE_DL_JSON + +/* Whether to build ldap as dynamic module */ +#undef COMPILE_DL_LDAP + +/* Whether to build libxml as dynamic module */ +#undef COMPILE_DL_LIBXML + +/* Whether to build mbstring as dynamic module */ +#undef COMPILE_DL_MBSTRING + +/* Whether to build mcrypt as dynamic module */ +#undef COMPILE_DL_MCRYPT + +/* Whether to build mssql as dynamic module */ +#undef COMPILE_DL_MSSQL + +/* Whether to build mysql as dynamic module */ +#undef COMPILE_DL_MYSQL + +/* Whether to build mysqli as dynamic module */ +#undef COMPILE_DL_MYSQLI + +/* Whether to build mysqlnd as dynamic module */ +#undef COMPILE_DL_MYSQLND + +/* Whether to build oci8 as dynamic module */ +#undef COMPILE_DL_OCI8 + +/* Whether to build odbc as dynamic module */ +#undef COMPILE_DL_ODBC + +/* Whether to build openssl as dynamic module */ +#undef COMPILE_DL_OPENSSL + +/* Whether to build pcntl as dynamic module */ +#undef COMPILE_DL_PCNTL + +/* Whether to build pcre as dynamic module */ +#undef COMPILE_DL_PCRE + +/* Whether to build pdo as dynamic module */ +#undef COMPILE_DL_PDO + +/* Whether to build pdo_dblib as dynamic module */ +#undef COMPILE_DL_PDO_DBLIB + +/* Whether to build pdo_firebird as dynamic module */ +#undef COMPILE_DL_PDO_FIREBIRD + +/* Whether to build pdo_mysql as dynamic module */ +#undef COMPILE_DL_PDO_MYSQL + +/* Whether to build pdo_oci as dynamic module */ +#undef COMPILE_DL_PDO_OCI + +/* Whether to build pdo_odbc as dynamic module */ +#undef COMPILE_DL_PDO_ODBC + +/* Whether to build pdo_pgsql as dynamic module */ +#undef COMPILE_DL_PDO_PGSQL + +/* Whether to build pdo_sqlite as dynamic module */ +#undef COMPILE_DL_PDO_SQLITE + +/* Whether to build pgsql as dynamic module */ +#undef COMPILE_DL_PGSQL + +/* Whether to build phar as dynamic module */ +#undef COMPILE_DL_PHAR + +/* Whether to build posix as dynamic module */ +#undef COMPILE_DL_POSIX + +/* Whether to build pspell as dynamic module */ +#undef COMPILE_DL_PSPELL + +/* Whether to build readline as dynamic module */ +#undef COMPILE_DL_READLINE + +/* Whether to build recode as dynamic module */ +#undef COMPILE_DL_RECODE + +/* Whether to build reflection as dynamic module */ +#undef COMPILE_DL_REFLECTION + +/* Whether to build session as dynamic module */ +#undef COMPILE_DL_SESSION + +/* Whether to build shmop as dynamic module */ +#undef COMPILE_DL_SHMOP + +/* Whether to build simplexml as dynamic module */ +#undef COMPILE_DL_SIMPLEXML + +/* Whether to build snmp as dynamic module */ +#undef COMPILE_DL_SNMP + +/* Whether to build soap as dynamic module */ +#undef COMPILE_DL_SOAP + +/* Whether to build sockets as dynamic module */ +#undef COMPILE_DL_SOCKETS + +/* Whether to build spl as dynamic module */ +#undef COMPILE_DL_SPL + +/* Whether to build sqlite3 as dynamic module */ +#undef COMPILE_DL_SQLITE3 + +/* Whether to build standard as dynamic module */ +#undef COMPILE_DL_STANDARD + +/* Whether to build sybase_ct as dynamic module */ +#undef COMPILE_DL_SYBASE_CT + +/* Whether to build sysvmsg as dynamic module */ +#undef COMPILE_DL_SYSVMSG + +/* Whether to build sysvsem as dynamic module */ +#undef COMPILE_DL_SYSVSEM + +/* Whether to build sysvshm as dynamic module */ +#undef COMPILE_DL_SYSVSHM + +/* Whether to build tidy as dynamic module */ +#undef COMPILE_DL_TIDY + +/* Whether to build tokenizer as dynamic module */ +#undef COMPILE_DL_TOKENIZER + +/* Whether to build wddx as dynamic module */ +#undef COMPILE_DL_WDDX + +/* Whether to build xml as dynamic module */ +#undef COMPILE_DL_XML + +/* Whether to build xmlreader as dynamic module */ +#undef COMPILE_DL_XMLREADER + +/* Whether to build xmlrpc as dynamic module */ +#undef COMPILE_DL_XMLRPC + +/* Whether to build xmlwriter as dynamic module */ +#undef COMPILE_DL_XMLWRITER + +/* Whether to build xsl as dynamic module */ +#undef COMPILE_DL_XSL + +/* Whether to build zip as dynamic module */ +#undef COMPILE_DL_ZIP + +/* Whether to build zlib as dynamic module */ +#undef COMPILE_DL_ZLIB + +/* */ +#undef COOKIE_IO_FUNCTIONS_T + +/* */ +#undef COOKIE_SEEKER_USES_OFF64_T + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#undef CRAY_STACKSEG_END + +/* Define if crypt_r has uses CRYPTD */ +#undef CRYPT_R_CRYPTD + +/* Define if struct crypt_data requires _GNU_SOURCE */ +#undef CRYPT_R_GNU_SOURCE + +/* Define if crypt_r uses struct crypt_data */ +#undef CRYPT_R_STRUCT_CRYPT_DATA + +/* Define to 1 if using `alloca.c'. */ +#undef C_ALLOCA + +/* Define if the target system is darwin */ +#undef DARWIN + +/* */ +#undef DB1_INCLUDE_FILE + +/* */ +#undef DB1_VERSION + +/* */ +#undef DB2_INCLUDE_FILE + +/* */ +#undef DB3_INCLUDE_FILE + +/* */ +#undef DB4_INCLUDE_FILE + +/* */ +#undef DBA_CDB + +/* */ +#undef DBA_CDB_BUILTIN + +/* */ +#undef DBA_CDB_MAKE + +/* */ +#undef DBA_DB1 + +/* */ +#undef DBA_DB2 + +/* */ +#undef DBA_DB3 + +/* */ +#undef DBA_DB4 + +/* */ +#undef DBA_DBM + +/* */ +#undef DBA_FLATFILE + +/* */ +#undef DBA_GDBM + +/* */ +#undef DBA_INIFILE + +/* */ +#undef DBA_NDBM + +/* */ +#undef DBA_QDBM + +/* */ +#undef DBA_TCADB + +/* */ +#undef DBM_INCLUDE_FILE + +/* */ +#undef DBM_VERSION + +/* */ +#undef DEFAULT_SHORT_OPEN_TAG + +/* Define if dlsym() requires a leading underscore in symbol names. */ +#undef DLSYM_NEEDS_UNDERSCORE + +/* Whether to enable chroot() function */ +#undef ENABLE_CHROOT_FUNC + +/* */ +#undef ENABLE_GD_TTF + +/* */ +#undef ENCHANT_VERSION_STRING + +/* */ +#undef GDBM_INCLUDE_FILE + +/* Whether you use GNU Pth */ +#undef GNUPTH + +/* Whether 3 arg set_rebind_proc() */ +#undef HAVE_3ARG_SETREBINDPROC + +/* Define to 1 if you have the `acosh' function. */ +#undef HAVE_ACOSH + +/* */ +#undef HAVE_ADABAS + +/* whether the compiler supports __alignof__ */ +#undef HAVE_ALIGNOF + +/* Define to 1 if you have `alloca', as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the `alphasort' function. */ +#undef HAVE_ALPHASORT + +/* Whether you have AOLserver */ +#undef HAVE_AOLSERVER + +/* */ +#undef HAVE_APACHE + +/* */ +#undef HAVE_APACHE_HOOKS + +/* Define to 1 if you have the + header file. */ +#undef HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H + +/* */ +#undef HAVE_AP_COMPAT_H + +/* */ +#undef HAVE_AP_CONFIG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_NAMESER_H + +/* Define to 1 if you have the `asctime_r' function. */ +#undef HAVE_ASCTIME_R + +/* Define to 1 if you have the `asinh' function. */ +#undef HAVE_ASINH + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_ASSERT_H + +/* Define to 1 if you have the `atanh' function. */ +#undef HAVE_ATANH + +/* whether atof() accepts INF */ +#undef HAVE_ATOF_ACCEPTS_INF + +/* whether atof() accepts NAN */ +#undef HAVE_ATOF_ACCEPTS_NAN + +/* Define to 1 if you have the `atoll' function. */ +#undef HAVE_ATOLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_ATOMIC_H + +/* whether the compiler supports __attribute__ ((__aligned__)) */ +#undef HAVE_ATTRIBUTE_ALIGNED + +/* Whether you have bcmath */ +#undef HAVE_BCMATH + +/* */ +#undef HAVE_BIND_TEXTDOMAIN_CODESET + +/* */ +#undef HAVE_BIRDSTEP + +/* Define if system has broken getcwd */ +#undef HAVE_BROKEN_GETCWD + +/* Define if your glibc borks on fopen with mode a+ */ +#undef HAVE_BROKEN_GLIBC_FOPEN_APPEND + +/* Whether we have librecode 3.5 */ +#undef HAVE_BROKEN_RECODE + +/* Konstantin Chuguev's iconv implementation */ +#undef HAVE_BSD_ICONV + +/* */ +#undef HAVE_BUILD_DEFS_H + +/* Define to 1 if gcc supports __sync_bool_compare_and_swap() a.o. */ +#undef HAVE_BUILTIN_ATOMIC + +/* */ +#undef HAVE_BUNDLED_PCRE + +/* */ +#undef HAVE_BZ2 + +/* */ +#undef HAVE_CALENDAR + +/* Whether to compile with Caudium support */ +#undef HAVE_CAUDIUM + +/* Define to 1 if you have the `chroot' function. */ +#undef HAVE_CHROOT + +/* Define to 1 if you have the `clearenv' function. */ +#undef HAVE_CLEARENV + +/* */ +#undef HAVE_CLI0CLI_H + +/* */ +#undef HAVE_CLI0CORE_H + +/* */ +#undef HAVE_CLI0DEFS_H + +/* */ +#undef HAVE_CLI0ENV_H + +/* */ +#undef HAVE_CLI0EXT_H + +/* do we have clock_gettime? */ +#undef HAVE_CLOCK_GETTIME + +/* do we have clock_get_time? */ +#undef HAVE_CLOCK_GET_TIME + +/* Whether you have struct cmsghdr */ +#undef HAVE_CMSGHDR + +/* */ +#undef HAVE_CODBC + +/* */ +#undef HAVE_COLORCLOSESTHWB + +/* Whether you have a Continuity Server */ +#undef HAVE_CONTINUITY + +/* Define to 1 if you have the `CreateProcess' function. */ +#undef HAVE_CREATEPROCESS + +/* */ +#undef HAVE_CRYPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRYPT_H + +/* Define to 1 if you have the `crypt_r' function. */ +#undef HAVE_CRYPT_R + +/* Define to 1 if you have the `ctermid' function. */ +#undef HAVE_CTERMID + +/* Define to 1 if you have the `ctime_r' function. */ +#undef HAVE_CTIME_R + +/* */ +#undef HAVE_CTYPE + +/* */ +#undef HAVE_CURL + +/* */ +#undef HAVE_CURL_EASY_STRERROR + +/* Have cURL with GnuTLS support */ +#undef HAVE_CURL_GNUTLS + +/* */ +#undef HAVE_CURL_MULTI_STRERROR + +/* Have cURL with OpenSSL support */ +#undef HAVE_CURL_OPENSSL + +/* Have cURL with SSL support */ +#undef HAVE_CURL_SSL + +/* */ +#undef HAVE_CURL_VERSION_INFO + +/* Define to 1 if you have the `cuserid' function. */ +#undef HAVE_CUSERID + +/* */ +#undef HAVE_DBA + +/* Whether you want DBMaker */ +#undef HAVE_DBMAKER + +/* */ +#undef HAVE_DCNGETTEXT + +/* Whether system headers declare timezone */ +#undef HAVE_DECLARED_TIMEZONE + +/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. + */ +#undef HAVE_DECL_TZNAME + +/* do we have /dev/poll? */ +#undef HAVE_DEVPOLL + +/* Define if the target system has /dev/arandom device */ +#undef HAVE_DEV_ARANDOM + +/* Define if the target system has /dev/urandom device */ +#undef HAVE_DEV_URANDOM + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* */ +#undef HAVE_DLOPEN + +/* Whether you have dmalloc */ +#undef HAVE_DMALLOC + +/* */ +#undef HAVE_DNGETTEXT + +/* Define to 1 if you have the header file. */ +#undef HAVE_DNS_H + +/* */ +#undef HAVE_DNS_SEARCH + +/* */ +#undef HAVE_DN_EXPAND + +/* */ +#undef HAVE_DN_SKIPNAME + +/* */ +#undef HAVE_DOM + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* OpenSSL 0.9.7 or later */ +#undef HAVE_DSA_DEFAULT_METHOD + +/* Whether to enable DTrace support */ +#undef HAVE_DTRACE + +/* embedded MySQL support enabled */ +#undef HAVE_EMBEDDED_MYSQLI + +/* */ +#undef HAVE_EMPRESS + +/* */ +#undef HAVE_ENCHANT + +/* */ +#undef HAVE_ENCHANT_BROKER_SET_PARAM + +/* do we have epoll? */ +#undef HAVE_EPOLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERRNO_H + +/* */ +#undef HAVE_ESOOB + +/* Whether you want EXIF (metadata from images) support */ +#undef HAVE_EXIF + +/* Define to 1 if you have the `fabsf' function. */ +#undef HAVE_FABSF + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `finite' function. */ +#undef HAVE_FINITE + +/* Define to 1 if you have the `flock' function. */ +#undef HAVE_FLOCK + +/* Define to 1 if you have the `floorf' function. */ +#undef HAVE_FLOORF + +/* Define if flush should be called explicitly after a buffered io. */ +#undef HAVE_FLUSHIO + +/* Define to 1 if your system has a working POSIX `fnmatch' function. */ +#undef HAVE_FNMATCH + +/* */ +#undef HAVE_FOPENCOOKIE + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `fpclass' function. */ +#undef HAVE_FPCLASS + +/* whether fpsetprec is present and usable */ +#undef HAVE_FPSETPREC + +/* whether FPU control word can be manipulated by inline assembler */ +#undef HAVE_FPU_INLINE_ASM_X86 + +/* whether floatingpoint.h defines fp_except */ +#undef HAVE_FP_EXCEPT + +/* */ +#undef HAVE_FREETDS + +/* Define to 1 if you have the `ftok' function. */ +#undef HAVE_FTOK + +/* Whether you want FTP support */ +#undef HAVE_FTP + +/* Define to 1 if you have the `funopen' function. */ +#undef HAVE_FUNOPEN + +/* Define to 1 if you have the `gai_strerror' function. */ +#undef HAVE_GAI_STRERROR + +/* Whether you have gcov */ +#undef HAVE_GCOV + +/* Define to 1 if you have the `gcvt' function. */ +#undef HAVE_GCVT + +/* */ +#undef HAVE_GDIMAGECOLORRESOLVE + +/* */ +#undef HAVE_GD_BUNDLED + +/* */ +#undef HAVE_GD_CACHE_CREATE + +/* */ +#undef HAVE_GD_DYNAMIC_CTX_EX + +/* */ +#undef HAVE_GD_FONTCACHESHUTDOWN + +/* */ +#undef HAVE_GD_FONTMUTEX + +/* */ +#undef HAVE_GD_FREEFONTCACHE + +/* */ +#undef HAVE_GD_GD2 + +/* */ +#undef HAVE_GD_GIF_CREATE + +/* */ +#undef HAVE_GD_GIF_CTX + +/* */ +#undef HAVE_GD_GIF_READ + +/* */ +#undef HAVE_GD_IMAGEELLIPSE + +/* */ +#undef HAVE_GD_IMAGESETBRUSH + +/* */ +#undef HAVE_GD_IMAGESETTILE + +/* */ +#undef HAVE_GD_JPG + +/* */ +#undef HAVE_GD_PNG + +/* */ +#undef HAVE_GD_STRINGFT + +/* */ +#undef HAVE_GD_STRINGFTEX + +/* */ +#undef HAVE_GD_STRINGTTF + +/* */ +#undef HAVE_GD_WBMP + +/* */ +#undef HAVE_GD_WEBP + +/* */ +#undef HAVE_GD_XBM + +/* */ +#undef HAVE_GD_XPM + +/* Define if you have the getaddrinfo function */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getgrgid_r' function. */ +#undef HAVE_GETGRGID_R + +/* Define to 1 if you have the `getgrnam_r' function. */ +#undef HAVE_GETGRNAM_R + +/* Define to 1 if you have the `getgroups' function. */ +#undef HAVE_GETGROUPS + +/* */ +#undef HAVE_GETHOSTBYADDR + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getloadavg' function. */ +#undef HAVE_GETLOADAVG + +/* Define to 1 if you have the `getlogin' function. */ +#undef HAVE_GETLOGIN + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the `getpgid' function. */ +#undef HAVE_GETPGID + +/* Define to 1 if you have the `getpid' function. */ +#undef HAVE_GETPID + +/* Define to 1 if you have the `getpriority' function. */ +#undef HAVE_GETPRIORITY + +/* Define to 1 if you have the `getprotobyname' function. */ +#undef HAVE_GETPROTOBYNAME + +/* Define to 1 if you have the `getprotobynumber' function. */ +#undef HAVE_GETPROTOBYNUMBER + +/* Define to 1 if you have the `getpwnam_r' function. */ +#undef HAVE_GETPWNAM_R + +/* Define to 1 if you have the `getpwuid_r' function. */ +#undef HAVE_GETPWUID_R + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Define to 1 if you have the `getservbyname' function. */ +#undef HAVE_GETSERVBYNAME + +/* Define to 1 if you have the `getservbyport' function. */ +#undef HAVE_GETSERVBYPORT + +/* Define to 1 if you have the `getsid' function. */ +#undef HAVE_GETSID + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `getwd' function. */ +#undef HAVE_GETWD + +/* */ +#undef HAVE_GICONV_H + +/* glibc's iconv implementation */ +#undef HAVE_GLIBC_ICONV + +/* Define to 1 if you have the `glob' function. */ +#undef HAVE_GLOB + +/* */ +#undef HAVE_GMP + +/* Define to 1 if you have the `gmtime_r' function. */ +#undef HAVE_GMTIME_R + +/* Define to 1 if you have the `grantpt' function. */ +#undef HAVE_GRANTPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GRP_H + +/* Have HASH Extension */ +#undef HAVE_HASH_EXT + +/* Define to 1 if you have the `hstrerror' function. */ +#undef HAVE_HSTRERROR + +/* */ +#undef HAVE_HTONL + +/* whether HUGE_VAL == INF */ +#undef HAVE_HUGE_VAL_INF + +/* whether HUGE_VAL + -HUGEVAL == NAN */ +#undef HAVE_HUGE_VAL_NAN + +/* Define to 1 if you have the `hypot' function. */ +#undef HAVE_HYPOT + +/* */ +#undef HAVE_IBASE + +/* */ +#undef HAVE_IBMDB2 + +/* IBM iconv implementation */ +#undef HAVE_IBM_ICONV + +/* */ +#undef HAVE_ICONV + +/* Define to 1 if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define to 1 if you have the `if_indextoname' function. */ +#undef HAVE_IF_INDEXTONAME + +/* Define to 1 if you have the `if_nametoindex' function. */ +#undef HAVE_IF_NAMETOINDEX + +/* */ +#undef HAVE_IMAP + +/* */ +#undef HAVE_IMAP2000 + +/* */ +#undef HAVE_IMAP2001 + +/* */ +#undef HAVE_IMAP2004 + +/* */ +#undef HAVE_IMAP_AUTH_GSS + +/* */ +#undef HAVE_IMAP_KRB + +/* */ +#undef HAVE_IMAP_MUTF7 + +/* */ +#undef HAVE_IMAP_SSL + +/* */ +#undef HAVE_INET_ATON + +/* Define to 1 if you have the `inet_ntoa' function. */ +#undef HAVE_INET_NTOA + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the `initgroups' function. */ +#undef HAVE_INITGROUPS + +/* Define if int32_t type is present. */ +#undef HAVE_INT32_T + +/* Whether intmax_t is available */ +#undef HAVE_INTMAX_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* */ +#undef HAVE_IODBC + +/* */ +#undef HAVE_IODBC_H + +/* Whether to enable IPv6 support */ +#undef HAVE_IPV6 + +/* Define to 1 if you have the `isascii' function. */ +#undef HAVE_ISASCII + +/* Define to 1 if you have the `isfinite' function. */ +#undef HAVE_ISFINITE + +/* Define to 1 if you have the `isinf' function. */ +#undef HAVE_ISINF + +/* Define to 1 if you have the `isnan' function. */ +#undef HAVE_ISNAN + +/* */ +#undef HAVE_ISQLEXT_H + +/* */ +#undef HAVE_ISQL_H + +/* whether to enable JavaScript Object Serialization support */ +#undef HAVE_JSON + +/* Define to 1 if you have the `kill' function. */ +#undef HAVE_KILL + +/* do we have kqueue? */ +#undef HAVE_KQUEUE + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the `lchown' function. */ +#undef HAVE_LCHOWN + +/* */ +#undef HAVE_LDAP + +/* Define to 1 if you have the `ldap_parse_reference' function. */ +#undef HAVE_LDAP_PARSE_REFERENCE + +/* Define to 1 if you have the `ldap_parse_result' function. */ +#undef HAVE_LDAP_PARSE_RESULT + +/* LDAP SASL support */ +#undef HAVE_LDAP_SASL + +/* */ +#undef HAVE_LDAP_SASL_H + +/* */ +#undef HAVE_LDAP_SASL_SASL_H + +/* Define to 1 if you have the `ldap_start_tls_s' function. */ +#undef HAVE_LDAP_START_TLS_S + +/* */ +#undef HAVE_LIBBIND + +/* */ +#undef HAVE_LIBCRYPT + +/* */ +#undef HAVE_LIBDL + +/* */ +#undef HAVE_LIBDNET_STUB + +/* */ +#undef HAVE_LIBEDIT + +/* */ +#undef HAVE_LIBEXPAT + +/* */ +#undef HAVE_LIBFREETYPE + +/* */ +#undef HAVE_LIBGD + +/* */ +#undef HAVE_LIBGD13 + +/* */ +#undef HAVE_LIBGD15 + +/* */ +#undef HAVE_LIBGD20 + +/* */ +#undef HAVE_LIBGD204 + +/* */ +#undef HAVE_LIBICONV + +/* */ +#undef HAVE_LIBINTL + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* */ +#undef HAVE_LIBMCRYPT + +/* Whether you have libmm */ +#undef HAVE_LIBMM + +/* */ +#undef HAVE_LIBNSL + +/* */ +#undef HAVE_LIBPAM + +/* */ +#undef HAVE_LIBRARYMANAGER_H + +/* */ +#undef HAVE_LIBREADLINE + +/* Whether we have librecode 3.5 or higher */ +#undef HAVE_LIBRECODE + +/* */ +#undef HAVE_LIBRESOLV + +/* */ +#undef HAVE_LIBRT + +/* */ +#undef HAVE_LIBSOCKET + +/* */ +#undef HAVE_LIBT1 + +/* */ +#undef HAVE_LIBXML + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `link' function. */ +#undef HAVE_LINK + +/* Define to 1 if you have the `localeconv' function. */ +#undef HAVE_LOCALECONV + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +/* Define to 1 if you have the `lockf' function. */ +#undef HAVE_LOCKF + +/* Define to 1 if you have the `log1p' function. */ +#undef HAVE_LOG1P + +/* do we have SO_LISTENQxxx? */ +#undef HAVE_LQ_SO_LISTENQ + +/* do we have TCP_INFO? */ +#undef HAVE_LQ_TCP_INFO + +/* Define to 1 if you have the `lrand48' function. */ +#undef HAVE_LRAND48 + +/* do we have mach_vm_read? */ +#undef HAVE_MACH_VM_READ + +/* Define to 1 if you have the `makedev' function. */ +#undef HAVE_MAKEDEV + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the `mblen' function. */ +#undef HAVE_MBLEN + +/* whether to have multibyte regex support */ +#undef HAVE_MBREGEX + +/* Define to 1 if you have the `mbrlen' function. */ +#undef HAVE_MBRLEN + +/* Define to 1 if you have the `mbsinit' function. */ +#undef HAVE_MBSINIT + +/* Define if your system has mbstate_t in wchar.h */ +#undef HAVE_MBSTATE_T + +/* whether to have multibyte string support */ +#undef HAVE_MBSTRING + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define if the target system has support for memory allocation using + mmap(MAP_ANON) */ +#undef HAVE_MEM_MMAP_ANON + +/* Define if the target system has support for memory allocation using + mmap("/dev/zero") */ +#undef HAVE_MEM_MMAP_ZERO + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if you have the `mknod' function. */ +#undef HAVE_MKNOD + +/* Define to 1 if you have the `mkstemp' function. */ +#undef HAVE_MKSTEMP + +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_MONETARY_H + +/* Define to 1 if you have the `mremap' function. */ +#undef HAVE_MREMAP + +/* */ +#undef HAVE_MSSQL + +/* Whether you have MySQL */ +#undef HAVE_MYSQL + +/* */ +#undef HAVE_MYSQLILIB + +/* Define to 1 if you have the `nanosleep' function. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_TCP_H + +/* Whether utf8_mime2text() has new signature */ +#undef HAVE_NEW_MIME2TEXT + +/* */ +#undef HAVE_NGETTEXT + +/* Define to 1 if you have the `nice' function. */ +#undef HAVE_NICE + +/* Define to 1 if you have the `nl_langinfo' function. */ +#undef HAVE_NL_LANGINFO + +/* Whether you have a Netscape/iPlanet/Sun Webserver */ +#undef HAVE_NSAPI + +/* */ +#undef HAVE_NSLDAP + +/* */ +#undef HAVE_OCI8 + +/* */ +#undef HAVE_OCICOLLASSIGN + +/* */ +#undef HAVE_OCIENVCREATE + +/* */ +#undef HAVE_OCIENVNLSCREATE + +/* */ +#undef HAVE_OCILOBISTEMPORARY + +/* */ +#undef HAVE_OCISTMTFETCH2 + +/* */ +#undef HAVE_OCI_INSTANT_CLIENT + +/* */ +#undef HAVE_OCI_LOB_READ2 + +/* */ +#undef HAVE_ODBC2 + +/* */ +#undef HAVE_ODBCSDK_H + +/* */ +#undef HAVE_ODBC_H + +/* */ +#undef HAVE_ODBC_ROUTER + +/* */ +#undef HAVE_OLD_COMPAT_H + +/* whether you have old-style readdir_r */ +#undef HAVE_OLD_READDIR_R + +/* Define to 1 if the oniguruma library is available */ +#undef HAVE_ONIG + +/* Define to 1 if you have the header file. */ +#undef HAVE_OPENSSL_CRYPTO_H + +/* */ +#undef HAVE_OPENSSL_EXT + +/* */ +#undef HAVE_ORALDAP + +/* */ +#undef HAVE_ORALDAP_10 + +/* Whether struct _zend_object_value is packed */ +#undef HAVE_PACKED_OBJECT_VALUE + +/* */ +#undef HAVE_PCRE + +/* */ +#undef HAVE_PDO_DBLIB + +/* */ +#undef HAVE_PDO_FIREBIRD + +/* Whether to build PostgreSQL for PDO support or not */ +#undef HAVE_PDO_PGSQL + +/* */ +#undef HAVE_PDO_SQLITELIB + +/* Define to 1 if you have the `perror' function. */ +#undef HAVE_PERROR + +/* Whether to build PostgreSQL support or not */ +#undef HAVE_PGSQL + +/* Whether libpq is compiled with --enable-multibyte */ +#undef HAVE_PGSQL_WITH_MULTIBYTE_SUPPORT + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PGTRANSACTIONSTATUS + +/* Whether to have pg_config.h */ +#undef HAVE_PG_CONFIG_H + +/* PostgreSQL 8.1 or later */ +#undef HAVE_PG_LO_CREATE + +/* PostgreSQL 8.4 or later */ +#undef HAVE_PG_LO_IMPORT_WITH_OID + +/* */ +#undef HAVE_PHP_SESSION + +/* Whether you have phttpd */ +#undef HAVE_PHTTPD + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* do we have port framework? */ +#undef HAVE_PORT + +/* whether to include POSIX-like functions */ +#undef HAVE_POSIX + +/* whether you have POSIX readdir_r */ +#undef HAVE_POSIX_READDIR_R + +/* PostgreSQL 7.0.x or later */ +#undef HAVE_PQCLIENTENCODING + +/* Broken libpq under windows */ +#undef HAVE_PQCMDTUPLES + +/* PostgreSQL 7.2.0 or later */ +#undef HAVE_PQESCAPE + +/* PostgreSQL 9.0 or later */ +#undef HAVE_PQESCAPELITERAL + +/* PostgreSQL 8.1.4 or later */ +#undef HAVE_PQESCAPE_BYTEA_CONN + +/* PostgreSQL 8.1.4 or later */ +#undef HAVE_PQESCAPE_CONN + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQEXECPARAMS + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQEXECPREPARED + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQFREEMEM + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQFTABLE + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQGETCOPYDATA + +/* Older PostgreSQL */ +#undef HAVE_PQOIDVALUE + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQPARAMETERSTATUS + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQPREPARE + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQPROTOCOLVERSION + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQPUTCOPYDATA + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQPUTCOPYEND + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQRESULTERRORFIELD + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQSENDPREPARE + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQSENDQUERYPARAMS + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQSENDQUERYPREPARED + +/* PostgreSQL 7.4 or later */ +#undef HAVE_PQSETERRORVERBOSITY + +/* PostgreSQL 7.0.x or later */ +#undef HAVE_PQSETNONBLOCKING + +/* PostgreSQL 7.3.0 or later */ +#undef HAVE_PQUNESCAPEBYTEA + +/* do we have prctl? */ +#undef HAVE_PRCTL + +/* */ +#undef HAVE_PREAD + +/* */ +#undef HAVE_PSPELL + +/* do we have ptrace? */ +#undef HAVE_PTRACE + +/* Whether ptrdiff_t is available */ +#undef HAVE_PTRDIFF_T + +/* Define to 1 if you have the `ptsname' function. */ +#undef HAVE_PTSNAME + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* */ +#undef HAVE_PWRITE + +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + +/* Define to 1 if you have the `rand_r' function. */ +#undef HAVE_RAND_R + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Whether Reflection is enabled */ +#undef HAVE_REFLECTION + +/* 1 */ +#undef HAVE_REGEX_T_RE_MAGIC + +/* Define to 1 if you have the header file. */ +#undef HAVE_RESOLV_H + +/* */ +#undef HAVE_RES_NSEARCH + +/* */ +#undef HAVE_RES_SEARCH + +/* */ +#undef HAVE_RFC822_OUTPUT_ADDRESS_LIST + +/* */ +#undef HAVE_RL_CALLBACK_READ_CHAR + +/* Define to 1 if you have the `rl_completion_matches' function. */ +#undef HAVE_RL_COMPLETION_MATCHES + +/* */ +#undef HAVE_RL_ON_NEW_LINE + +/* Whether you use Roxen */ +#undef HAVE_ROXEN + +/* */ +#undef HAVE_SAPDB + +/* Whether you have sockaddr_storage.ss_family */ +#undef HAVE_SA_SS_FAMILY + +/* Define to 1 if you have the `scandir' function. */ +#undef HAVE_SCANDIR + +/* do we have select? */ +#undef HAVE_SELECT + +/* */ +#undef HAVE_SEMUN + +/* Define to 1 if you have the `setegid' function. */ +#undef HAVE_SETEGID + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `seteuid' function. */ +#undef HAVE_SETEUID + +/* Define to 1 if you have the `setitimer' function. */ +#undef HAVE_SETITIMER + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setpgid' function. */ +#undef HAVE_SETPGID + +/* Define to 1 if you have the `setpriority' function. */ +#undef HAVE_SETPRIORITY + +/* Define to 1 if you have the `setproctitle' function. */ +#undef HAVE_SETPROCTITLE + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* Define to 1 if you have the `setsockopt' function. */ +#undef HAVE_SETSOCKOPT + +/* Define to 1 if you have the `setvbuf' function. */ +#undef HAVE_SETVBUF + +/* */ +#undef HAVE_SHMOP + +/* Define to 1 if you have the `shutdown' function. */ +#undef HAVE_SHUTDOWN + +/* */ +#undef HAVE_SHUTDOWN_SNMP_LOGGING + +/* Whether sigaction() is available */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the header file. */ +#undef HAVE_SIGNAL_H + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if you have the `sigsetjmp' function. */ +#undef HAVE_SIGSETJMP + +/* Define to 1 if you have the `sigtimedwait' function. */ +#undef HAVE_SIGTIMEDWAIT + +/* Define to 1 if you have the `sigwaitinfo' function. */ +#undef HAVE_SIGWAITINFO + +/* */ +#undef HAVE_SIMPLEXML + +/* Define to 1 if you have the `sin' function. */ +#undef HAVE_SIN + +/* */ +#undef HAVE_SNMP + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* */ +#undef HAVE_SOAP + +/* Whether struct sockaddr has field sa_len */ +#undef HAVE_SOCKADDR_SA_LEN + +/* Whether you have struct sockaddr_storage */ +#undef HAVE_SOCKADDR_STORAGE + +/* Define if sockaddr_un in sys/un.h contains a sun_len component */ +#undef HAVE_SOCKADDR_UN_SUN_LEN + +/* */ +#undef HAVE_SOCKET + +/* Define to 1 if you have the `socketpair' function. */ +#undef HAVE_SOCKETPAIR + +/* */ +#undef HAVE_SOCKETS + +/* Whether you have socklen_t */ +#undef HAVE_SOCKLEN_T + +/* */ +#undef HAVE_SOLID + +/* */ +#undef HAVE_SOLID_30 + +/* */ +#undef HAVE_SOLID_35 + +/* Whether you want SPL (Standard PHP Library) support */ +#undef HAVE_SPL + +/* */ +#undef HAVE_SQLCLI1_H + +/* */ +#undef HAVE_SQLDATASOURCES + +/* */ +#undef HAVE_SQLEXT_H + +/* */ +#undef HAVE_SQLITE3 + +/* have commercial sqlite3 with crypto support */ +#undef HAVE_SQLITE3_KEY + +/* */ +#undef HAVE_SQLTYPES_H + +/* */ +#undef HAVE_SQLUCODE_H + +/* */ +#undef HAVE_SQLUNIX_H + +/* */ +#undef HAVE_SQL_H + +/* Define to 1 if you have the `srand48' function. */ +#undef HAVE_SRAND48 + +/* Define to 1 if you have the `srandom' function. */ +#undef HAVE_SRANDOM + +/* Whether ssize_t is available */ +#undef HAVE_SSIZE_T + +/* Define to 1 if you have the `statfs' function. */ +#undef HAVE_STATFS + +/* Define to 1 if you have the `statvfs' function. */ +#undef HAVE_STATVFS + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_PROTOTYPES + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `std_syslog' function. */ +#undef HAVE_STD_SYSLOG + +/* */ +#undef HAVE_STMT_NEXT_RESULT + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strcoll' function. */ +#undef HAVE_STRCOLL + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strfmon' function. */ +#undef HAVE_STRFMON + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the `strnlen' function. */ +#undef HAVE_STRNLEN + +/* Define to 1 if you have the `strpbrk' function. */ +#undef HAVE_STRPBRK + +/* Define to 1 if you have the `strpncpy' function. */ +#undef HAVE_STRPNCPY + +/* Define to 1 if you have the `strptime' function. */ +#undef HAVE_STRPTIME + +/* whether strptime() declaration fails */ +#undef HAVE_STRPTIME_DECL_FAILS + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtod' function. */ +#undef HAVE_STRTOD + +/* Define to 1 if you have the `strtok_r' function. */ +#undef HAVE_STRTOK_R + +/* Define to 1 if you have the `strtol' function. */ +#undef HAVE_STRTOL + +/* Define to 1 if you have the `strtoll' function. */ +#undef HAVE_STRTOLL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if you have the `strtoull' function. */ +#undef HAVE_STRTOULL + +/* whether you have struct flock */ +#undef HAVE_STRUCT_FLOCK + +/* Define to 1 if `st_blksize' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLKSIZE + +/* Define to 1 if `st_blocks' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_BLOCKS + +/* Define to 1 if `st_rdev' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_RDEV + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +#undef HAVE_STRUCT_TM_TM_ZONE + +/* Define to 1 if your `struct stat' has `st_blksize'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLKSIZE' instead. */ +#undef HAVE_ST_BLKSIZE + +/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use + `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ +#undef HAVE_ST_BLOCKS + +/* Define to 1 if you have the header file. */ +#undef HAVE_ST_H + +/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use + `HAVE_STRUCT_STAT_ST_RDEV' instead. */ +#undef HAVE_ST_RDEV + +/* */ +#undef HAVE_SYBASE_CT + +/* Define to 1 if you have the `symlink' function. */ +#undef HAVE_SYMLINK + +/* Define if you have the __sync_fetch_and_add function */ +#undef HAVE_SYNC_FETCH_AND_ADD + +/* do we have sysconf? */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSEXITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* */ +#undef HAVE_SYSVMSG + +/* */ +#undef HAVE_SYSVSEM + +/* */ +#undef HAVE_SYSVSHM + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_LOADAVG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MKDEV_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MOUNT_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SDT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STATFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STATVFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSEXITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_VARARGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_VFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the `tempnam' function. */ +#undef HAVE_TEMPNAM + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* */ +#undef HAVE_TIDY + +/* */ +#undef HAVE_TIDYOPTGETDOC + +/* do we have times? */ +#undef HAVE_TIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + +/* whether you have tm_gmtoff in struct tm */ +#undef HAVE_TM_GMTOFF + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#undef HAVE_TM_ZONE + +/* Whether you have a working ttyname_r */ +#undef HAVE_TTYNAME_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_TUXMODULE_H + +/* Define to 1 if you don't have `tm_zone' but do have the external array + `tzname'. */ +#undef HAVE_TZNAME + +/* Define to 1 if you have the `tzset' function. */ +#undef HAVE_TZSET + +/* */ +#undef HAVE_UDBCEXT_H + +/* Define if uint32_t type is present. */ +#undef HAVE_UINT32_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* */ +#undef HAVE_UNIXODBC + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNIX_H + +/* Define to 1 if you have the `unlockpt' function. */ +#undef HAVE_UNLOCKPT + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* */ +#undef HAVE_UODBC + +/* Define to 1 if you have the `usleep' function. */ +#undef HAVE_USLEEP + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimes' function. */ +#undef HAVE_UTIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ +#undef HAVE_UTIME_NULL + +/* Wether struct utsname has domainname */ +#undef HAVE_UTSNAME_DOMAINNAME + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `wait3' function. */ +#undef HAVE_WAIT3 + +/* */ +#undef HAVE_WAITPID + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + +/* */ +#undef HAVE_WDDX + +/* */ +#undef HAVE_XML + +/* Define to 1 if you have the header file. */ +#undef HAVE_XMLPARSE_H + +/* */ +#undef HAVE_XMLREADER + +/* */ +#undef HAVE_XMLRPC + +/* Define to 1 if you have the header file. */ +#undef HAVE_XMLTOK_H + +/* */ +#undef HAVE_XMLWRITER + +/* */ +#undef HAVE_XSL + +/* */ +#undef HAVE_XSL_EXSLT + +/* */ +#undef HAVE_YP_GET_DEFAULT_DOMAIN + +/* */ +#undef HAVE_ZIP + +/* */ +#undef HAVE_ZLIB + +/* whether _controlfp is present usable */ +#undef HAVE__CONTROLFP + +/* whether _controlfp_s is present and usable */ +#undef HAVE__CONTROLFP_S + +/* whether _FPU_SETCW is present and usable */ +#undef HAVE__FPU_SETCW + +/* */ +#undef HPUX + +/* */ +#undef HSREGEX + +/* iconv() is aliased to libiconv() in -liconv */ +#undef ICONV_ALIASED_LIBICONV + +/* Whether iconv supports error no or not */ +#undef ICONV_SUPPORTS_ERRNO + +/* */ +#undef ISOLARIS + +/* */ +#undef LINUX + +/* Whether asctime_r is declared */ +#undef MISSING_ASCTIME_R_DECL + +/* Whether ctime_r is declared */ +#undef MISSING_CTIME_R_DECL + +/* */ +#undef MISSING_FCLOSE_DECL + +/* Whether gmtime_r is declared */ +#undef MISSING_GMTIME_R_DECL + +/* Whether localtime_r is declared */ +#undef MISSING_LOCALTIME_R_DECL + +/* */ +#undef MISSING_MSGHDR_MSGFLAGS + +/* Whether strtok_r is declared */ +#undef MISSING_STRTOK_R_DECL + +/* Whether mysqlnd is enabled */ +#undef MYSQLI_USE_MYSQLND + +/* Enable compressed protocol support */ +#undef MYSQLND_COMPRESSION_WANTED + +/* Enable SSL support */ +#undef MYSQLND_SSL_SUPPORTED + +/* Whether mysqlnd is enabled */ +#undef MYSQL_USE_MYSQLND + +/* */ +#undef NDBM_INCLUDE_FILE + +/* */ +#undef NEUTRINO + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* */ +#undef PDO_MYSQL_UNIX_ADDR + +/* Whether pdo_mysql uses mysqlnd */ +#undef PDO_USE_MYSQLND + +/* */ +#undef PHAR_HASH_OK + +/* */ +#undef PHAR_HAVE_OPENSSL + +/* */ +#undef PHP_APACHE_HAVE_CLIENT_FD + +/* Whether the system supports BlowFish salt */ +#undef PHP_BLOWFISH_CRYPT + +/* PHP build date */ +#undef PHP_BUILD_DATE + +/* Define if your system has fork/vfork/CreateProcess */ +#undef PHP_CAN_SUPPORT_PROC_OPEN + +/* */ +#undef PHP_CURL_URL_WRAPPERS + +/* Whether the system supports extended DES salt */ +#undef PHP_EXT_DES_CRYPT + +/* fpm group name */ +#undef PHP_FPM_GROUP + +/* fpm user name */ +#undef PHP_FPM_USER + +/* Whether you have HP-UX 10.x */ +#undef PHP_HPUX_TIME_R + +/* Path to iconv.h */ +#undef PHP_ICONV_H_PATH + +/* Which iconv implementation to use */ +#undef PHP_ICONV_IMPL + +/* Whether you have IRIX-style functions */ +#undef PHP_IRIX_TIME_R + +/* Whether the system supports MD5 salt */ +#undef PHP_MD5_CRYPT + +/* */ +#undef PHP_MHASH_BC + +/* */ +#undef PHP_MYSQL_UNIX_SOCK_ADDR + +/* */ +#undef PHP_OCI8_DEF_DIR + +/* */ +#undef PHP_OCI8_DEF_SHARED_LIBADD + +/* define to 1 if oniguruma has an invalid entry for KOI8 encoding */ +#undef PHP_ONIG_BAD_KOI8_ENTRY + +/* Define to 1 if the bundled oniguruma is used */ +#undef PHP_ONIG_BUNDLED + +/* uname output */ +#undef PHP_OS + +/* */ +#undef PHP_PDO_OCI_CLIENT_VERSION + +/* whether pread64 is default */ +#undef PHP_PREAD_64 + +/* whether pwrite64 is default */ +#undef PHP_PWRITE_64 + +/* Whether the system supports SHA256 salt */ +#undef PHP_SHA256_CRYPT + +/* Whether the system supports SHA512 salt */ +#undef PHP_SHA512_CRYPT + +/* */ +#undef PHP_SIGCHILD + +/* Whether the system supports standard DES salt */ +#undef PHP_STD_DES_CRYPT + +/* uname -a output */ +#undef PHP_UNAME + +/* Whether PHP has to use its own crypt_r for blowfish, des and ext des */ +#undef PHP_USE_PHP_CRYPT_R + +/* whether write(2) works */ +#undef PHP_WRITE_STDOUT + +/* /proc/pid/mem interface */ +#undef PROC_MEM_FILE + +/* Whether to use Pthreads */ +#undef PTHREADS + +/* */ +#undef QDBM_INCLUDE_FILE + +/* */ +#undef REGEX + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Whether to use Roxen in ZTS mode */ +#undef ROXEN_USE_ZTS + +/* The size of `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* Size of intmax_t */ +#undef SIZEOF_INTMAX_T + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `long int', as computed by sizeof. */ +#undef SIZEOF_LONG_INT + +/* The size of `long long', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG + +/* The size of `long long int', as computed by sizeof. */ +#undef SIZEOF_LONG_LONG_INT + +/* Size of ptrdiff_t */ +#undef SIZEOF_PTRDIFF_T + +/* The size of `short', as computed by sizeof. */ +#undef SIZEOF_SHORT + +/* The size of `size_t', as computed by sizeof. */ +#undef SIZEOF_SIZE_T + +/* Size of ssize_t */ +#undef SIZEOF_SSIZE_T + +/* */ +#undef SOLARIS + +/* have sqlite3 with column metadata enabled */ +#undef SQLITE_ENABLE_COLUMN_METADATA + +/* have sqlite3 with extension support */ +#undef SQLITE_OMIT_LOAD_EXTENSION + +/* Needed in sqlunix.h for wchar defs */ +#undef SS_FBX + +/* Needed in sqlunix.h */ +#undef SS_LINUX + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* */ +#undef TCADB_INCLUDE_FILE + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* */ +#undef TSRM_ST + +/* */ +#undef UNDEF_THREADS_HACK + +/* */ +#undef UNIXWARE + +/* whether to check multibyte regex backtrack */ +#undef USE_COMBINATION_EXPLOSION_CHECK + +/* */ +#undef USE_GD_IMGSTRTTF + +/* */ +#undef USE_GD_JISX0208 + +/* Define if cross-process locking is required by accept() */ +#undef USE_LOCKING + +/* */ +#undef USE_TRANSFER_TABLES + +/* whether you want Pi3Web support */ +#undef WITH_PI3WEB + +/* */ +#undef WITH_ZEUS + +/* Define if processor uses big-endian word */ +#undef WORDS_BIGENDIAN + +/* Whether sprintf is broken */ +#undef ZEND_BROKEN_SPRINTF + +/* */ +#undef ZEND_DEBUG + +/* Define if double cast to long preserves least significant bits */ +#undef ZEND_DVAL_TO_LVAL_CAST_OK + +/* */ +#undef ZEND_MM_ALIGNMENT + +/* */ +#undef ZEND_MM_ALIGNMENT_LOG2 + +/* Use zend signal handling */ +#undef ZEND_SIGNALS + +/* virtual machine dispatch method */ +#undef ZEND_VM_KIND + +/* */ +#undef ZTS + +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* */ +#undef in_addr_t + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define to `unsigned int ' if does not define. */ +#undef uint + +/* Define to `unsigned long ' if does not define. */ +#undef ulong + + +#ifndef ZEND_ACCONFIG_H_NO_C_PROTOS + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_IEEEFP_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#else +# include +#endif + +#if ZEND_BROKEN_SPRINTF +int zend_sprintf(char *buffer, const char *format, ...); +#else +# define zend_sprintf sprintf +#endif + +#include + +/* To enable the is_nan, is_infinite and is_finite PHP functions */ +#ifdef NETWARE + #define HAVE_ISNAN 1 + #define HAVE_ISINF 1 + #define HAVE_ISFINITE 1 +#endif + +#ifndef zend_isnan +#ifdef HAVE_ISNAN +#define zend_isnan(a) isnan(a) +#elif defined(HAVE_FPCLASS) +#define zend_isnan(a) ((fpclass(a) == FP_SNAN) || (fpclass(a) == FP_QNAN)) +#else +#define zend_isnan(a) 0 +#endif +#endif + +#ifdef HAVE_ISINF +#define zend_isinf(a) isinf(a) +#elif defined(INFINITY) +/* Might not work, but is required by ISO C99 */ +#define zend_isinf(a) (((a)==INFINITY)?1:0) +#elif defined(HAVE_FPCLASS) +#define zend_isinf(a) ((fpclass(a) == FP_PINF) || (fpclass(a) == FP_NINF)) +#else +#define zend_isinf(a) 0 +#endif + +#ifdef HAVE_FINITE +#define zend_finite(a) finite(a) +#elif defined(HAVE_ISFINITE) || defined(isfinite) +#define zend_finite(a) isfinite(a) +#elif defined(fpclassify) +#define zend_finite(a) ((fpclassify((a))!=FP_INFINITE&&fpclassify((a))!=FP_NAN)?1:0) +#else +#define zend_finite(a) (zend_isnan(a) ? 0 : zend_isinf(a) ? 0 : 1) +#endif + +#endif /* ifndef ZEND_ACCONFIG_H_NO_C_PROTOS */ + +#ifdef NETWARE +#ifdef USE_WINSOCK +#/*This detection against winsock is of no use*/ undef HAVE_SOCKLEN_T +#/*This detection against winsock is of no use*/ undef HAVE_SYS_SOCKET_H +#endif +#endif + +#undef PTHREADS + diff --git a/main/php_content_types.c b/main/php_content_types.c new file mode 100644 index 0000000..c443397 --- /dev/null +++ b/main/php_content_types.c @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +#include "php.h" +#include "SAPI.h" +#include "rfc1867.h" + +#include "php_content_types.h" + +/* {{{ php_post_entries[] + */ +static sapi_post_entry php_post_entries[] = { + { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data, php_std_post_handler }, + { MULTIPART_CONTENT_TYPE, sizeof(MULTIPART_CONTENT_TYPE)-1, NULL, rfc1867_post_handler }, + { NULL, 0, NULL, NULL } +}; +/* }}} */ + +/* {{{ SAPI_POST_READER_FUNC + */ +SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader) +{ + char *data; + int length; + + /* $HTTP_RAW_POST_DATA registration */ + if (!strcmp(SG(request_info).request_method, "POST")) { + if (NULL == SG(request_info).post_entry) { + /* no post handler registered, so we just swallow the data */ + sapi_read_standard_form_data(TSRMLS_C); + } + + /* For unknown content types we create HTTP_RAW_POST_DATA even if always_populate_raw_post_data off, + * this is in-effecient, but we need to keep doing it for BC reasons (for now) */ + if ((PG(always_populate_raw_post_data) || NULL == SG(request_info).post_entry) && SG(request_info).post_data) { + length = SG(request_info).post_data_length; + data = estrndup(SG(request_info).post_data, length); + SET_VAR_STRINGL("HTTP_RAW_POST_DATA", data, length); + } + } + + /* for php://input stream: + some post handlers modify the content of request_info.post_data + so for now we need a copy for the php://input stream + in the long run post handlers should be changed to not touch + request_info.post_data for memory preservation reasons + */ + if (SG(request_info).post_data) { + SG(request_info).raw_post_data = estrndup(SG(request_info).post_data, SG(request_info).post_data_length); + SG(request_info).raw_post_data_length = SG(request_info).post_data_length; + } +} +/* }}} */ + +/* {{{ php_startup_sapi_content_types + */ +int php_startup_sapi_content_types(TSRMLS_D) +{ + 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; +} +/* }}} */ + +/* {{{ php_setup_sapi_content_types + */ +int php_setup_sapi_content_types(TSRMLS_D) +{ + sapi_register_post_entries(php_post_entries TSRMLS_CC); + + return SUCCESS; +} +/* }}} */ + +/* + * 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_content_types.h b/main/php_content_types.h new file mode 100644 index 0000000..7ceffff --- /dev/null +++ b/main/php_content_types.h @@ -0,0 +1,31 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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 PHP_CONTENT_TYPES_H +#define PHP_CONTENT_TYPES_H + +#define DEFAULT_POST_CONTENT_TYPE "application/x-www-form-urlencoded" + +SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); +SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler); +int php_startup_sapi_content_types(TSRMLS_D); +int php_setup_sapi_content_types(TSRMLS_D); + +#endif /* PHP_CONTENT_TYPES_H */ diff --git a/main/php_getopt.h b/main/php_getopt.h new file mode 100644 index 0000000..a3f4c14 --- /dev/null +++ b/main/php_getopt.h @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_GETOPT_H +#define PHP_GETOPT_H + +#include "php.h" + +#ifdef NETWARE +/* +As NetWare LibC has optind and optarg macros defined in unistd.h our local variables were getting mistakenly preprocessed so undeffing optind and optarg +*/ +#undef optarg +#undef optind +#endif + +/* Define structure for one recognized option (both single char and long name). + * If short_open is '-' this is the last option. */ +typedef struct _opt_struct { + char opt_char; + int need_param; + char * opt_name; +} opt_struct; + +BEGIN_EXTERN_C() +/* holds the index of the latest fetched element from the opts array */ +extern PHPAPI int php_optidx; +PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char **optarg, int *optind, int show_err, int arg_start); +END_EXTERN_C() + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/php_globals.h b/main/php_globals.h new file mode 100644 index 0000000..170431d --- /dev/null +++ b/main/php_globals.h @@ -0,0 +1,175 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_GLOBALS_H +#define PHP_GLOBALS_H + +#include "zend_globals.h" + +typedef struct _php_core_globals php_core_globals; + +#ifdef ZTS +# define PG(v) TSRMG(core_globals_id, php_core_globals *, v) +extern PHPAPI int core_globals_id; +#else +# define PG(v) (core_globals.v) +extern ZEND_API struct _php_core_globals core_globals; +#endif + +/* Error display modes */ +#define PHP_DISPLAY_ERRORS_STDOUT 1 +#define PHP_DISPLAY_ERRORS_STDERR 2 + +/* Track vars */ +#define TRACK_VARS_POST 0 +#define TRACK_VARS_GET 1 +#define TRACK_VARS_COOKIE 2 +#define TRACK_VARS_SERVER 3 +#define TRACK_VARS_ENV 4 +#define TRACK_VARS_FILES 5 +#define TRACK_VARS_REQUEST 6 + +struct _php_tick_function_entry; + +typedef struct _arg_separators { + char *output; + char *input; +} arg_separators; + +struct _php_core_globals { + zend_bool implicit_flush; + + long output_buffering; + + zend_bool sql_safe_mode; + zend_bool enable_dl; + + char *output_handler; + + char *unserialize_callback_func; + long serialize_precision; + + long memory_limit; + long max_input_time; + + zend_bool track_errors; + zend_bool display_errors; + zend_bool display_startup_errors; + zend_bool log_errors; + long log_errors_max_len; + zend_bool ignore_repeated_errors; + zend_bool ignore_repeated_source; + zend_bool report_memleaks; + char *error_log; + + char *doc_root; + char *user_dir; + char *include_path; + char *open_basedir; + char *extension_dir; + char *php_binary; + + char *upload_tmp_dir; + long upload_max_filesize; + + char *error_append_string; + char *error_prepend_string; + + char *auto_prepend_file; + char *auto_append_file; + + arg_separators arg_separator; + + char *variables_order; + + HashTable rfc1867_protected_variables; + + short connection_status; + short ignore_user_abort; + + unsigned char header_is_being_sent; + + zend_llist tick_functions; + + zval *http_globals[6]; + + zend_bool expose_php; + + zend_bool register_argc_argv; + zend_bool auto_globals_jit; + + char *docref_root; + char *docref_ext; + + zend_bool html_errors; + zend_bool xmlrpc_errors; + + long xmlrpc_error_number; + + zend_bool activated_auto_globals[8]; + + zend_bool modules_activated; + 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; + + int last_error_type; + char *last_error_message; + char *last_error_file; + int last_error_lineno; + + char *disable_functions; + char *disable_classes; + zend_bool allow_url_include; + zend_bool exit_on_timeout; +#ifdef PHP_WIN32 + zend_bool com_initialized; +#endif + long max_input_nesting_level; + long max_input_vars; + zend_bool in_user_include; + + char *user_ini_filename; + long user_ini_cache_ttl; + + char *request_order; + + zend_bool mail_x_header; + char *mail_log; + + zend_bool in_error_log; + +#ifdef PHP_WIN32 + zend_bool windows_show_crt_warning; +#endif +}; + + +#endif /* PHP_GLOBALS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/php_ini.c b/main/php_ini.c new file mode 100644 index 0000000..b15a384 --- /dev/null +++ b/main/php_ini.c @@ -0,0 +1,916 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "ext/standard/info.h" +#include "zend_ini.h" +#include "zend_ini_scanner.h" +#include "php_ini.h" +#include "ext/standard/dl.h" +#include "zend_extensions.h" +#include "zend_highlight.h" +#include "SAPI.h" +#include "php_main.h" +#include "php_scandir.h" +#ifdef PHP_WIN32 +#include "win32/php_registry.h" +#endif + +#if HAVE_SCANDIR && HAVE_ALPHASORT && HAVE_DIRENT_H +#include +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifdef PHP_WIN32 +#define TRANSLATE_SLASHES_LOWER(path) \ + { \ + char *tmp = path; \ + while (*tmp) { \ + if (*tmp == '\\') *tmp = '/'; \ + else *tmp = tolower(*tmp); \ + tmp++; \ + } \ + } +#else +#define TRANSLATE_SLASHES_LOWER(path) +#endif + + +typedef struct _php_extension_lists { + zend_llist engine; + zend_llist functions; +} php_extension_lists; + +/* True globals */ +static int is_special_section = 0; +static HashTable *active_ini_hash; +static HashTable configuration_hash; +static int has_per_dir_config = 0; +static int has_per_host_config = 0; +PHPAPI char *php_ini_opened_path=NULL; +static php_extension_lists extension_lists; +PHPAPI char *php_ini_scanned_path=NULL; +PHPAPI char *php_ini_scanned_files=NULL; + +/* {{{ php_ini_displayer_cb + */ +static void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type TSRMLS_DC) +{ + if (ini_entry->displayer) { + ini_entry->displayer(ini_entry, type); + } else { + char *display_string; + uint display_string_length, esc_html=0; + + if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { + if (ini_entry->orig_value && ini_entry->orig_value[0]) { + display_string = ini_entry->orig_value; + display_string_length = ini_entry->orig_value_length; + esc_html = !sapi_module.phpinfo_as_text; + } else { + if (!sapi_module.phpinfo_as_text) { + display_string = "no value"; + display_string_length = sizeof("no value") - 1; + } else { + display_string = "no value"; + display_string_length = sizeof("no value") - 1; + } + } + } else if (ini_entry->value && ini_entry->value[0]) { + display_string = ini_entry->value; + display_string_length = ini_entry->value_length; + esc_html = !sapi_module.phpinfo_as_text; + } else { + if (!sapi_module.phpinfo_as_text) { + display_string = "no value"; + display_string_length = sizeof("no value") - 1; + } else { + display_string = "no value"; + display_string_length = sizeof("no value") - 1; + } + } + + if (esc_html) { + php_html_puts(display_string, display_string_length TSRMLS_CC); + } else { + PHPWRITE(display_string, display_string_length); + } + } +} +/* }}} */ + +/* {{{ php_ini_displayer + */ +static int php_ini_displayer(zend_ini_entry *ini_entry, int module_number TSRMLS_DC) +{ + if (ini_entry->module_number != module_number) { + return 0; + } + if (!sapi_module.phpinfo_as_text) { + PUTS(""); + PUTS(""); + PHPWRITE(ini_entry->name, ini_entry->name_length - 1); + PUTS(""); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC); + PUTS(""); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC); + PUTS("\n"); + } else { + PHPWRITE(ini_entry->name, ini_entry->name_length - 1); + PUTS(" => "); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC); + PUTS(" => "); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC); + PUTS("\n"); + } + return 0; +} +/* }}} */ + +/* {{{ php_ini_available + */ +static int php_ini_available(zend_ini_entry *ini_entry, int *module_number_available TSRMLS_DC) +{ + if (ini_entry->module_number == *module_number_available) { + *module_number_available = -1; + return ZEND_HASH_APPLY_STOP; + } else { + return ZEND_HASH_APPLY_KEEP; + } +} +/* }}} */ + +/* {{{ display_ini_entries + */ +PHPAPI void display_ini_entries(zend_module_entry *module) +{ + int module_number, module_number_available; + TSRMLS_FETCH(); + + if (module) { + module_number = module->module_number; + } else { + module_number = 0; + } + module_number_available = module_number; + zend_hash_apply_with_argument(EG(ini_directives), (apply_func_arg_t) php_ini_available, &module_number_available TSRMLS_CC); + if (module_number_available == -1) { + php_info_print_table_start(); + php_info_print_table_header(3, "Directive", "Local Value", "Master Value"); + zend_hash_apply_with_argument(EG(ini_directives), (apply_func_arg_t) php_ini_displayer, (void *) (zend_intptr_t) module_number TSRMLS_CC); + php_info_print_table_end(); + } +} +/* }}} */ + +/* php.ini support */ +#define PHP_EXTENSION_TOKEN "extension" +#define ZEND_EXTENSION_TOKEN "zend_extension" + +/* {{{ config_zval_dtor + */ +PHPAPI void config_zval_dtor(zval *zvalue) +{ + if (Z_TYPE_P(zvalue) == IS_ARRAY) { + zend_hash_destroy(Z_ARRVAL_P(zvalue)); + free(Z_ARRVAL_P(zvalue)); + } else if (Z_TYPE_P(zvalue) == IS_STRING) { + free(Z_STRVAL_P(zvalue)); + } +} +/* Reset / free active_ini_sectin global */ +#define RESET_ACTIVE_INI_HASH() do { \ + active_ini_hash = NULL; \ + is_special_section = 0; \ +} while (0) +/* }}} */ + +/* {{{ php_ini_parser_cb + */ +static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, HashTable *target_hash) +{ + zval *entry; + HashTable *active_hash; + char *extension_name; + + if (active_ini_hash) { + active_hash = active_ini_hash; + } else { + active_hash = target_hash; + } + + switch (callback_type) { + case ZEND_INI_PARSER_ENTRY: { + if (!arg2) { + /* bare string - nothing to do */ + break; + } + + /* PHP and Zend extensions are not added into configuration hash! */ + if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), PHP_EXTENSION_TOKEN)) { /* load PHP extension */ + extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); + zend_llist_add_element(&extension_lists.functions, &extension_name); + } else if (!is_special_section && !strcasecmp(Z_STRVAL_P(arg1), ZEND_EXTENSION_TOKEN)) { /* load Zend extension */ + extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); + zend_llist_add_element(&extension_lists.engine, &extension_name); + + /* All other entries are added into either configuration_hash or active ini section array */ + } else { + /* Store in active hash */ + zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, arg2, sizeof(zval), (void **) &entry); + Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry)); + } + } + break; + + case ZEND_INI_PARSER_POP_ENTRY: { + zval *option_arr; + zval *find_arr; + + if (!arg2) { + /* bare string - nothing to do */ + break; + } + +/* fprintf(stdout, "ZEND_INI_PARSER_POP_ENTRY: %s[%s] = %s\n",Z_STRVAL_P(arg1), Z_STRVAL_P(arg3), Z_STRVAL_P(arg2)); */ + + /* If option not found in hash or is not an array -> create array, otherwise add to existing array */ + if (zend_hash_find(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void **) &find_arr) == FAILURE || Z_TYPE_P(find_arr) != IS_ARRAY) { + option_arr = (zval *) pemalloc(sizeof(zval), 1); + INIT_PZVAL(option_arr); + Z_TYPE_P(option_arr) = IS_ARRAY; + Z_ARRVAL_P(option_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(Z_ARRVAL_P(option_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1); + zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, option_arr, sizeof(zval), (void **) &find_arr); + free(option_arr); + } + + /* arg3 is possible option offset name */ + if (arg3 && Z_STRLEN_P(arg3) > 0) { + zend_symtable_update(Z_ARRVAL_P(find_arr), Z_STRVAL_P(arg3), Z_STRLEN_P(arg3) + 1, arg2, sizeof(zval), (void **) &entry); + } else { + zend_hash_next_index_insert(Z_ARRVAL_P(find_arr), arg2, sizeof(zval), (void **) &entry); + } + Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry)); + } + break; + + case ZEND_INI_PARSER_SECTION: { /* Create an array of entries of each section */ + +/* fprintf(stdout, "ZEND_INI_PARSER_SECTION: %s\n",Z_STRVAL_P(arg1)); */ + + char *key = NULL; + uint key_len; + + /* PATH sections */ + if (!strncasecmp(Z_STRVAL_P(arg1), "PATH", sizeof("PATH") - 1)) { + key = Z_STRVAL_P(arg1); + key = key + sizeof("PATH") - 1; + key_len = Z_STRLEN_P(arg1) - sizeof("PATH") + 1; + is_special_section = 1; + has_per_dir_config = 1; + + /* make the path lowercase on Windows, for case insensitivty. Does nothign for other platforms */ + TRANSLATE_SLASHES_LOWER(key); + + /* HOST sections */ + } else if (!strncasecmp(Z_STRVAL_P(arg1), "HOST", sizeof("HOST") - 1)) { + key = Z_STRVAL_P(arg1); + key = key + sizeof("HOST") - 1; + key_len = Z_STRLEN_P(arg1) - sizeof("HOST") + 1; + is_special_section = 1; + has_per_host_config = 1; + zend_str_tolower(key, key_len); /* host names are case-insensitive. */ + + } else { + is_special_section = 0; + } + + if (key && key_len > 0) { + /* Strip any trailing slashes */ + while (key_len > 0 && (key[key_len - 1] == '/' || key[key_len - 1] == '\\')) { + key_len--; + key[key_len] = 0; + } + + /* Strip any leading whitespace and '=' */ + while (*key && ( + *key == '=' || + *key == ' ' || + *key == '\t' + )) { + key++; + key_len--; + } + + /* Search for existing entry and if it does not exist create one */ + if (zend_hash_find(target_hash, key, key_len + 1, (void **) &entry) == FAILURE) { + zval *section_arr; + + section_arr = (zval *) pemalloc(sizeof(zval), 1); + INIT_PZVAL(section_arr); + Z_TYPE_P(section_arr) = IS_ARRAY; + Z_ARRVAL_P(section_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(Z_ARRVAL_P(section_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1); + zend_hash_update(target_hash, key, key_len + 1, section_arr, sizeof(zval), (void **) &entry); + free(section_arr); + } + active_ini_hash = Z_ARRVAL_P(entry); + } + } + break; + } +} +/* }}} */ + +/* {{{ php_load_php_extension_cb + */ +static void php_load_php_extension_cb(void *arg TSRMLS_DC) +{ +#ifdef HAVE_LIBDL + php_load_extension(*((char **) arg), MODULE_PERSISTENT, 0 TSRMLS_CC); +#endif +} +/* }}} */ + +/* {{{ php_load_zend_extension_cb + */ +static void php_load_zend_extension_cb(void *arg TSRMLS_DC) +{ + zend_load_extension(*((char **) arg)); +} +/* }}} */ + +/* {{{ php_init_config + */ +int php_init_config(TSRMLS_D) +{ + char *php_ini_file_name = NULL; + char *php_ini_search_path = NULL; + int php_ini_scanned_path_len; + char *open_basedir; + int free_ini_search_path = 0; + zend_file_handle fh; + + if (zend_hash_init(&configuration_hash, 0, NULL, (dtor_func_t) config_zval_dtor, 1) == FAILURE) { + return FAILURE; + } + + if (sapi_module.ini_defaults) { + sapi_module.ini_defaults(&configuration_hash); + } + + 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); + + open_basedir = PG(open_basedir); + + if (sapi_module.php_ini_path_override) { + php_ini_file_name = sapi_module.php_ini_path_override; + php_ini_search_path = sapi_module.php_ini_path_override; + free_ini_search_path = 0; + } else if (!sapi_module.php_ini_ignore) { + int search_path_size; + char *default_location; + char *env_location; + static const char paths_separator[] = { ZEND_PATHS_SEPARATOR, 0 }; +#ifdef PHP_WIN32 + char *reg_location; + char phprc_path[MAXPATHLEN]; +#endif + + env_location = getenv("PHPRC"); + +#ifdef PHP_WIN32 + if (!env_location) { + char dummybuf; + int size; + + SetLastError(0); + + /*If the given bugger is not large enough to hold the data, the return value is + the buffer size, in characters, required to hold the string and its terminating + null character. We use this return value to alloc the final buffer. */ + size = GetEnvironmentVariableA("PHPRC", &dummybuf, 0); + if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + /* The environment variable doesn't exist. */ + env_location = ""; + } else { + if (size == 0) { + env_location = ""; + } else { + size = GetEnvironmentVariableA("PHPRC", phprc_path, size); + if (size == 0) { + env_location = ""; + } else { + env_location = phprc_path; + } + } + } + } +#else + if (!env_location) { + env_location = ""; + } +#endif + /* + * Prepare search path + */ + + search_path_size = MAXPATHLEN * 4 + strlen(env_location) + 3 + 1; + php_ini_search_path = (char *) emalloc(search_path_size); + free_ini_search_path = 1; + php_ini_search_path[0] = 0; + + /* Add environment location */ + if (env_location[0]) { + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, env_location, search_path_size); + php_ini_file_name = env_location; + } + +#ifdef PHP_WIN32 + /* Add registry location */ + reg_location = GetIniPathFromRegistry(); + if (reg_location != NULL) { + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, reg_location, search_path_size); + efree(reg_location); + } +#endif + + /* Add cwd (not with CLI) */ + 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); + } + + 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; + } + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, binary_location, search_path_size); + efree(binary_location); + } + + /* Add default location */ +#ifdef PHP_WIN32 + default_location = (char *) emalloc(MAXPATHLEN + 1); + + if (0 < GetWindowsDirectory(default_location, MAXPATHLEN)) { + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, default_location, search_path_size); + } + + /* For people running under terminal services, GetWindowsDirectory will + * return their personal Windows directory, so lets add the system + * windows directory too */ + if (0 < GetSystemWindowsDirectory(default_location, MAXPATHLEN)) { + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, default_location, search_path_size); + } + efree(default_location); + +#else + default_location = PHP_CONFIG_FILE_PATH; + if (*php_ini_search_path) { + strlcat(php_ini_search_path, paths_separator, search_path_size); + } + strlcat(php_ini_search_path, default_location, search_path_size); +#endif + } + + PG(open_basedir) = NULL; + + /* + * Find and open actual ini file + */ + + memset(&fh, 0, sizeof(fh)); + + /* If SAPI does not want to ignore all ini files OR an overriding file/path is given. + * This allows disabling scanning for ini files in the PHP_CONFIG_FILE_SCAN_DIR but still + * load an optional ini file. */ + if (!sapi_module.php_ini_ignore || sapi_module.php_ini_path_override) { + + /* Check if php_ini_file_name is a file and can be opened */ + if (php_ini_file_name && php_ini_file_name[0]) { + struct stat statbuf; + + if (!VCWD_STAT(php_ini_file_name, &statbuf)) { + if (!((statbuf.st_mode & S_IFMT) == S_IFDIR)) { + fh.handle.fp = VCWD_FOPEN(php_ini_file_name, "r"); + if (fh.handle.fp) { + fh.filename = php_ini_opened_path = expand_filepath(php_ini_file_name, NULL TSRMLS_CC); + } + } + } + } + + /* Otherwise search for php-%sapi-module-name%.ini file in search path */ + if (!fh.handle.fp) { + const char *fmt = "php-%s.ini"; + char *ini_fname; + spprintf(&ini_fname, 0, fmt, sapi_module.name); + fh.handle.fp = php_fopen_with_path(ini_fname, "r", php_ini_search_path, &php_ini_opened_path TSRMLS_CC); + efree(ini_fname); + if (fh.handle.fp) { + fh.filename = php_ini_opened_path; + } + } + + /* If still no ini file found, search for php.ini file in search path */ + if (!fh.handle.fp) { + fh.handle.fp = php_fopen_with_path("php.ini", "r", php_ini_search_path, &php_ini_opened_path TSRMLS_CC); + if (fh.handle.fp) { + fh.filename = php_ini_opened_path; + } + } + } + + if (free_ini_search_path) { + efree(php_ini_search_path); + } + + PG(open_basedir) = open_basedir; + + if (fh.handle.fp) { + fh.type = ZEND_HANDLE_FP; + RESET_ACTIVE_INI_HASH(); + + zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC); + + { + zval tmp; + + Z_STRLEN(tmp) = strlen(fh.filename); + Z_STRVAL(tmp) = zend_strndup(fh.filename, Z_STRLEN(tmp)); + Z_TYPE(tmp) = IS_STRING; + Z_SET_REFCOUNT(tmp, 0); + + zend_hash_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path"), (void *) &tmp, sizeof(zval), NULL); + if (php_ini_opened_path) { + efree(php_ini_opened_path); + } + php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp)); + } + } + + /* Check for PHP_INI_SCAN_DIR environment variable to override/set config file scan directory */ + php_ini_scanned_path = getenv("PHP_INI_SCAN_DIR"); + if (!php_ini_scanned_path) { + /* Or fall back using possible --with-config-file-scan-dir setting (defaults to empty string!) */ + php_ini_scanned_path = PHP_CONFIG_FILE_SCAN_DIR; + } + php_ini_scanned_path_len = strlen(php_ini_scanned_path); + + /* Scan and parse any .ini files found in scan path if path not empty. */ + if (!sapi_module.php_ini_ignore && php_ini_scanned_path_len) { + struct dirent **namelist; + int ndir, i; + struct stat sb; + char ini_file[MAXPATHLEN]; + char *p; + zend_file_handle fh2; + zend_llist scanned_ini_list; + zend_llist_element *element; + int l, total_l = 0; + + if ((ndir = php_scandir(php_ini_scanned_path, &namelist, 0, php_alphasort)) > 0) { + zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); + memset(&fh2, 0, sizeof(fh2)); + + for (i = 0; i < ndir; i++) { + + /* check for any file with .ini extension */ + if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) { + free(namelist[i]); + continue; + } + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + + if (IS_SLASH(php_ini_scanned_path[php_ini_scanned_path_len - 1])) { + snprintf(ini_file, MAXPATHLEN, "%s%s", php_ini_scanned_path, namelist[i]->d_name); + } else { + snprintf(ini_file, MAXPATHLEN, "%s%c%s", php_ini_scanned_path, DEFAULT_SLASH, namelist[i]->d_name); + } + if (VCWD_STAT(ini_file, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + if ((fh2.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh2.filename = ini_file; + fh2.type = ZEND_HANDLE_FP; + + if (zend_parse_ini_file(&fh2, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC) == SUCCESS) { + /* Here, add it to the list of ini files read */ + l = strlen(ini_file); + total_l += l + 2; + p = estrndup(ini_file, l); + zend_llist_add_element(&scanned_ini_list, &p); + } + } + } + } + free(namelist[i]); + } + free(namelist); + + if (total_l) { + int php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; + php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); + if (!php_ini_scanned_files_len) { + *php_ini_scanned_files = '\0'; + } + total_l += php_ini_scanned_files_len; + for (element = scanned_ini_list.head; element; element = element->next) { + if (php_ini_scanned_files_len) { + strlcat(php_ini_scanned_files, ",\n", total_l); + } + strlcat(php_ini_scanned_files, *(char **)element->data, total_l); + strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l); + } + } + zend_llist_destroy(&scanned_ini_list); + } + } else { + /* Make sure an empty php_ini_scanned_path ends up as NULL */ + php_ini_scanned_path = NULL; + } + + if (sapi_module.ini_entries) { + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ php_shutdown_config + */ +int php_shutdown_config(void) +{ + zend_hash_destroy(&configuration_hash); + if (php_ini_opened_path) { + free(php_ini_opened_path); + php_ini_opened_path = NULL; + } + if (php_ini_scanned_files) { + free(php_ini_scanned_files); + php_ini_scanned_files = NULL; + } + return SUCCESS; +} +/* }}} */ + +/* {{{ php_ini_register_extensions + */ +void php_ini_register_extensions(TSRMLS_D) +{ + zend_llist_apply(&extension_lists.engine, php_load_zend_extension_cb TSRMLS_CC); + zend_llist_apply(&extension_lists.functions, php_load_php_extension_cb TSRMLS_CC); + + zend_llist_destroy(&extension_lists.engine); + zend_llist_destroy(&extension_lists.functions); +} +/* }}} */ + +/* {{{ php_parse_user_ini_file + */ +PHPAPI int php_parse_user_ini_file(const char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC) +{ + struct stat sb; + char ini_file[MAXPATHLEN]; + zend_file_handle fh; + + snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename); + + if (VCWD_STAT(ini_file, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + memset(&fh, 0, sizeof(fh)); + if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh.filename = ini_file; + fh.type = ZEND_HANDLE_FP; + + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + + if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash TSRMLS_CC) == SUCCESS) { + /* FIXME: Add parsed file to the list of user files read? */ + return SUCCESS; + } + return FAILURE; + } + } + } + return FAILURE; +} +/* }}} */ + +/* {{{ php_ini_activate_config + */ +PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC) +{ + char *str; + zval *data; + uint str_len; + ulong num_index; + + /* Walk through config hash and alter matching ini entries using the values found in the hash */ + for (zend_hash_internal_pointer_reset(source_hash); + zend_hash_get_current_key_ex(source_hash, &str, &str_len, &num_index, 0, NULL) == HASH_KEY_IS_STRING; + zend_hash_move_forward(source_hash) + ) { + zend_hash_get_current_data(source_hash, (void **) &data); + zend_alter_ini_entry_ex(str, str_len, Z_STRVAL_P(data), Z_STRLEN_P(data), modify_type, stage, 0 TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_ini_has_per_dir_config + */ +PHPAPI int php_ini_has_per_dir_config(void) +{ + return has_per_dir_config; +} +/* }}} */ + +/* {{{ php_ini_activate_per_dir_config + */ +PHPAPI void php_ini_activate_per_dir_config(char *path, uint path_len TSRMLS_DC) +{ + zval *tmp2; + char *ptr; + +#if PHP_WIN32 + char path_bak[MAXPATHLEN]; +#endif + + if (path_len > MAXPATHLEN) { + return; + } + +#if PHP_WIN32 + memcpy(path_bak, path, path_len); + path_bak[path_len] = 0; + TRANSLATE_SLASHES_LOWER(path_bak); + path = path_bak; +#endif + + /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */ + if (has_per_dir_config && path && path_len) { + ptr = path + 1; + while ((ptr = strchr(ptr, '/')) != NULL) { + *ptr = 0; + /* Search for source array matching the path from configuration_hash */ + if (zend_hash_find(&configuration_hash, path, strlen(path) + 1, (void **) &tmp2) == SUCCESS) { + php_ini_activate_config(Z_ARRVAL_P(tmp2), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE TSRMLS_CC); + } + *ptr = '/'; + ptr++; + } + } +} +/* }}} */ + +/* {{{ php_ini_has_per_host_config + */ +PHPAPI int php_ini_has_per_host_config(void) +{ + return has_per_host_config; +} +/* }}} */ + +/* {{{ php_ini_activate_per_host_config + */ +PHPAPI void php_ini_activate_per_host_config(const char *host, uint host_len TSRMLS_DC) +{ + zval *tmp; + + if (has_per_host_config && host && host_len) { + /* Search for source array matching the host from configuration_hash */ + if (zend_hash_find(&configuration_hash, host, host_len, (void **) &tmp) == SUCCESS) { + php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE TSRMLS_CC); + } + } +} +/* }}} */ + +/* {{{ cfg_get_entry + */ +PHPAPI zval *cfg_get_entry(const char *name, uint name_length) +{ + zval *tmp; + + if (zend_hash_find(&configuration_hash, name, name_length, (void **) &tmp) == SUCCESS) { + return tmp; + } else { + return NULL; + } +} +/* }}} */ + +/* {{{ cfg_get_long + */ +PHPAPI int cfg_get_long(const char *varname, long *result) +{ + zval *tmp, var; + + if (zend_hash_find(&configuration_hash, varname, strlen(varname) + 1, (void **) &tmp) == FAILURE) { + *result = 0; + return FAILURE; + } + var = *tmp; + zval_copy_ctor(&var); + convert_to_long(&var); + *result = Z_LVAL(var); + return SUCCESS; +} +/* }}} */ + +/* {{{ cfg_get_double + */ +PHPAPI int cfg_get_double(const char *varname, double *result) +{ + zval *tmp, var; + + if (zend_hash_find(&configuration_hash, varname, strlen(varname) + 1, (void **) &tmp) == FAILURE) { + *result = (double) 0; + return FAILURE; + } + var = *tmp; + zval_copy_ctor(&var); + convert_to_double(&var); + *result = Z_DVAL(var); + return SUCCESS; +} +/* }}} */ + +/* {{{ cfg_get_string + */ +PHPAPI int cfg_get_string(const char *varname, char **result) +{ + zval *tmp; + + if (zend_hash_find(&configuration_hash, varname, strlen(varname)+1, (void **) &tmp) == FAILURE) { + *result = NULL; + return FAILURE; + } + *result = Z_STRVAL_P(tmp); + return SUCCESS; +} +/* }}} */ + +PHPAPI HashTable* php_ini_get_configuration_hash(void) /* {{{ */ +{ + return &configuration_hash; +} /* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/main/php_ini.h b/main/php_ini.h new file mode 100644 index 0000000..65c80f7 --- /dev/null +++ b/main/php_ini.h @@ -0,0 +1,90 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_INI_H +#define PHP_INI_H + +#include "zend_ini.h" + +BEGIN_EXTERN_C() +PHPAPI void config_zval_dtor(zval *zvalue); +int php_init_config(TSRMLS_D); +int php_shutdown_config(void); +void php_ini_register_extensions(TSRMLS_D); +PHPAPI zval *cfg_get_entry(const char *name, uint name_length); +PHPAPI int cfg_get_long(const char *varname, long *result); +PHPAPI int cfg_get_double(const char *varname, double *result); +PHPAPI int cfg_get_string(const char *varname, char **result); +PHPAPI int php_parse_user_ini_file(const char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC); +PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC); +PHPAPI int php_ini_has_per_dir_config(void); +PHPAPI int php_ini_has_per_host_config(void); +PHPAPI void php_ini_activate_per_dir_config(char *path, uint path_len TSRMLS_DC); +PHPAPI void php_ini_activate_per_host_config(const char *host, uint host_len TSRMLS_DC); +PHPAPI HashTable* php_ini_get_configuration_hash(void); +END_EXTERN_C() + +#define PHP_INI_USER ZEND_INI_USER +#define PHP_INI_PERDIR ZEND_INI_PERDIR +#define PHP_INI_SYSTEM ZEND_INI_SYSTEM + +#define PHP_INI_ALL ZEND_INI_ALL + +#define php_ini_entry zend_ini_entry + +#define PHP_INI_MH ZEND_INI_MH +#define PHP_INI_DISP ZEND_INI_DISP + +#define PHP_INI_BEGIN ZEND_INI_BEGIN +#define PHP_INI_END ZEND_INI_END + +#define PHP_INI_ENTRY3_EX ZEND_INI_ENTRY3_EX +#define PHP_INI_ENTRY3 ZEND_INI_ENTRY3 +#define PHP_INI_ENTRY2_EX ZEND_INI_ENTRY2_EX +#define PHP_INI_ENTRY2 ZEND_INI_ENTRY2 +#define PHP_INI_ENTRY1_EX ZEND_INI_ENTRY1_EX +#define PHP_INI_ENTRY1 ZEND_INI_ENTRY1 +#define PHP_INI_ENTRY_EX ZEND_INI_ENTRY_EX +#define PHP_INI_ENTRY ZEND_INI_ENTRY + +#define STD_PHP_INI_ENTRY STD_ZEND_INI_ENTRY +#define STD_PHP_INI_ENTRY_EX STD_ZEND_INI_ENTRY_EX +#define STD_PHP_INI_BOOLEAN STD_ZEND_INI_BOOLEAN + +#define PHP_INI_DISPLAY_ORIG ZEND_INI_DISPLAY_ORIG +#define PHP_INI_DISPLAY_ACTIVE ZEND_INI_DISPLAY_ACTIVE + +#define PHP_INI_STAGE_STARTUP ZEND_INI_STAGE_STARTUP +#define PHP_INI_STAGE_SHUTDOWN ZEND_INI_STAGE_SHUTDOWN +#define PHP_INI_STAGE_ACTIVATE ZEND_INI_STAGE_ACTIVATE +#define PHP_INI_STAGE_DEACTIVATE ZEND_INI_STAGE_DEACTIVATE +#define PHP_INI_STAGE_RUNTIME ZEND_INI_STAGE_RUNTIME +#define PHP_INI_STAGE_HTACCESS ZEND_INI_STAGE_HTACCESS + +#define php_ini_boolean_displayer_cb zend_ini_boolean_displayer_cb +#define php_ini_color_displayer_cb zend_ini_color_displayer_cb + +#define php_alter_ini_entry zend_alter_ini_entry + +#define php_ini_long zend_ini_long +#define php_ini_double zend_ini_double +#define php_ini_string zend_ini_string + +#endif /* PHP_INI_H */ diff --git a/main/php_logos.c b/main/php_logos.c new file mode 100644 index 0000000..5162c6c --- /dev/null +++ b/main/php_logos.c @@ -0,0 +1,99 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Hartmut Holzgraefe | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "logos.h" +#include "php_logos.h" +#include "ext/standard/info.h" +#include "SAPI.h" + +typedef struct _php_info_logo { + const char *mimetype; + int mimelen; + const unsigned char *data; + int size; +} php_info_logo; + +static HashTable phpinfo_logo_hash; + +PHPAPI int php_register_info_logo(char *logo_string, const char *mimetype, const unsigned char *data, int size) +{ + php_info_logo info_logo; + + info_logo.mimetype = mimetype; + info_logo.mimelen = strlen(mimetype); + info_logo.data = data; + info_logo.size = size; + + return zend_hash_add(&phpinfo_logo_hash, logo_string, strlen(logo_string), &info_logo, sizeof(php_info_logo), NULL); +} + +PHPAPI int php_unregister_info_logo(char *logo_string) +{ + return zend_hash_del(&phpinfo_logo_hash, logo_string, strlen(logo_string)); +} + +int php_init_info_logos(void) +{ + if(zend_hash_init(&phpinfo_logo_hash, 0, NULL, NULL, 1)==FAILURE) + return FAILURE; + + php_register_info_logo(PHP_LOGO_GUID , "image/gif", php_logo , sizeof(php_logo)); + php_register_info_logo(PHP_EGG_LOGO_GUID, "image/gif", php_egg_logo, sizeof(php_egg_logo)); + php_register_info_logo(ZEND_LOGO_GUID , "image/gif", zend_logo , sizeof(zend_logo)); + + return SUCCESS; +} + +int php_shutdown_info_logos(void) +{ + zend_hash_destroy(&phpinfo_logo_hash); + return SUCCESS; +} + +#define CONTENT_TYPE_HEADER "Content-Type: " +int php_info_logos(const char *logo_string TSRMLS_DC) +{ + php_info_logo *logo_image; + char *content_header; + int len; + + if(FAILURE==zend_hash_find(&phpinfo_logo_hash, (char *) logo_string, strlen(logo_string), (void **)&logo_image)) + return 0; + + len = sizeof(CONTENT_TYPE_HEADER) - 1 + logo_image->mimelen; + content_header = emalloc(len + 1); + memcpy(content_header, CONTENT_TYPE_HEADER, sizeof(CONTENT_TYPE_HEADER) - 1); + memcpy(content_header + sizeof(CONTENT_TYPE_HEADER) - 1 , logo_image->mimetype, logo_image->mimelen); + content_header[len] = '\0'; + sapi_add_header(content_header, len, 0); + + PHPWRITE((char*)logo_image->data, logo_image->size); + return 1; +} + +/* + * 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_logos.h b/main/php_logos.h new file mode 100644 index 0000000..d0f0b5e --- /dev/null +++ b/main/php_logos.h @@ -0,0 +1,34 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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 _PHP_LOGOS_H +#define _PHP_LOGOS_H + +BEGIN_EXTERN_C() +PHPAPI int php_register_info_logo(char *logo_string, const char *mimetype, const unsigned char *data, int size); +PHPAPI int php_unregister_info_logo(char *logo_string); +END_EXTERN_C() + +int php_init_info_logos(void); +int php_shutdown_info_logos(void); +int php_info_logos(const char *logo_string TSRMLS_DC); + +#endif /* _PHP_LOGOS_H */ diff --git a/main/php_main.h b/main/php_main.h new file mode 100644 index 0000000..68ae837 --- /dev/null +++ b/main/php_main.h @@ -0,0 +1,61 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_MAIN_H +#define PHP_MAIN_H + +#include "zend_globals.h" +#include "php_globals.h" +#include "SAPI.h" + +BEGIN_EXTERN_C() +PHPAPI int php_request_startup(TSRMLS_D); +PHPAPI void php_request_shutdown(void *dummy); +PHPAPI void php_request_shutdown_for_exec(void *dummy); +PHPAPI int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules); +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); + +PHPAPI int php_execute_script(zend_file_handle *primary_file TSRMLS_DC); +PHPAPI int php_execute_simple_script(zend_file_handle *primary_file, zval **ret TSRMLS_DC); +PHPAPI int php_handle_special_queries(TSRMLS_D); +PHPAPI int php_lint_script(zend_file_handle *file TSRMLS_DC); + +PHPAPI void php_handle_aborted_connection(void); +PHPAPI int php_handle_auth_data(const char *auth TSRMLS_DC); + +PHPAPI void php_html_puts(const char *str, uint siz TSRMLS_DC); +PHPAPI int php_stream_open_for_zend_ex(const char *filename, zend_file_handle *handle, int mode TSRMLS_DC); + +extern void php_call_shutdown_functions(TSRMLS_D); +extern void php_free_shutdown_functions(TSRMLS_D); + +/* environment module */ +extern int php_init_environ(void); +extern int php_shutdown_environ(void); +END_EXTERN_C() + +#endif diff --git a/main/php_memory_streams.h b/main/php_memory_streams.h new file mode 100644 index 0000000..0f975cc --- /dev/null +++ b/main/php_memory_streams.h @@ -0,0 +1,68 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_MEMORY_STREAM_H +#define PHP_MEMORY_STREAM_H + +#include "php_streams.h" + +#define PHP_STREAM_MAX_MEM 2 * 1024 * 1024 + +#define TEMP_STREAM_DEFAULT 0 +#define TEMP_STREAM_READONLY 1 +#define TEMP_STREAM_TAKE_BUFFER 2 + +#define php_stream_memory_create(mode) _php_stream_memory_create((mode) STREAMS_CC TSRMLS_CC) +#define php_stream_memory_create_rel(mode) _php_stream_memory_create((mode) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_memory_open(mode, buf, length) _php_stream_memory_open((mode), (buf), (length) STREAMS_CC TSRMLS_CC) +#define php_stream_memory_get_buffer(stream, length) _php_stream_memory_get_buffer((stream), (length) STREAMS_CC TSRMLS_CC) + +#define php_stream_temp_new() php_stream_temp_create(TEMP_STREAM_DEFAULT, PHP_STREAM_MAX_MEM) +#define php_stream_temp_create(mode, max_memory_usage) _php_stream_temp_create((mode), (max_memory_usage) STREAMS_CC TSRMLS_CC) +#define php_stream_temp_create_rel(mode, max_memory_usage) _php_stream_temp_create((mode), (max_memory_usage) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_temp_open(mode, max_memory_usage, buf, length) _php_stream_temp_open((mode), (max_memory_usage), (buf), (length) STREAMS_CC TSRMLS_CC) + +BEGIN_EXTERN_C() +PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC); +PHPAPI php_stream *_php_stream_memory_open(int mode, char *buf, size_t length STREAMS_DC TSRMLS_DC); +PHPAPI char *_php_stream_memory_get_buffer(php_stream *stream, size_t *length STREAMS_DC TSRMLS_DC); + +PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC TSRMLS_DC); +PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC TSRMLS_DC); +END_EXTERN_C() + +extern PHPAPI php_stream_ops php_stream_memory_ops; +extern PHPAPI php_stream_ops php_stream_temp_ops; +extern PHPAPI php_stream_ops php_stream_rfc2397_ops; +extern PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper; + +#define PHP_STREAM_IS_MEMORY &php_stream_memory_ops +#define PHP_STREAM_IS_TEMP &php_stream_temp_ops + +#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/php_network.h b/main/php_network.h new file mode 100644 index 0000000..8ffb51c --- /dev/null +++ b/main/php_network.h @@ -0,0 +1,321 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Stig Venaas | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef _PHP_NETWORK_H +#define _PHP_NETWORK_H + +#ifdef PHP_WIN32 +# include "win32/inet.h" +#else +# undef closesocket +# define closesocket close +#endif + +#ifndef HAVE_SHUTDOWN +#undef shutdown +#define shutdown(s,n) /* nothing */ +#endif + +#ifdef PHP_WIN32 +# ifdef EWOULDBLOCK +# undef EWOULDBLOCK +# endif +# ifdef EINPROGRESS +# undef EINPROGRESS +# endif +# define EWOULDBLOCK WSAEWOULDBLOCK +# define EINPROGRESS WSAEWOULDBLOCK +# define fsync _commit +# define ftruncate(a, b) chsize(a, b) +#endif /* defined(PHP_WIN32) */ + +#ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN +#endif + +#ifdef PHP_WIN32 +#define php_socket_errno() WSAGetLastError() +#else +#define php_socket_errno() errno +#endif + +/* like strerror, but caller must efree the returned string, + * unless buf is not NULL. + * Also works sensibly for win32 */ +BEGIN_EXTERN_C() +PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize); +END_EXTERN_C() + +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +/* These are here, rather than with the win32 counterparts above, + * since defines them. */ +#ifndef SHUT_RD +# define SHUT_RD 0 +# define SHUT_WR 1 +# define SHUT_RDWR 2 +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef PHP_WIN32 +typedef SOCKET php_socket_t; +#else +typedef int php_socket_t; +#endif + +#ifdef PHP_WIN32 +# define SOCK_ERR INVALID_SOCKET +# define SOCK_CONN_ERR SOCKET_ERROR +# define SOCK_RECV_ERR SOCKET_ERROR +#else +# define SOCK_ERR -1 +# define SOCK_CONN_ERR -1 +# define SOCK_RECV_ERR -1 +#endif + +/* uncomment this to debug poll(2) emulation on systems that have poll(2) */ +/* #define PHP_USE_POLL_2_EMULATION 1 */ + +#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) +# include +typedef struct pollfd php_pollfd; +#else +typedef struct _php_pollfd { + php_socket_t fd; + short events; + short revents; +} php_pollfd; + +PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout); + +#ifndef POLLIN +# define POLLIN 0x0001 /* There is data to read */ +# define POLLPRI 0x0002 /* There is urgent data to read */ +# define POLLOUT 0x0004 /* Writing now will not block */ +# define POLLERR 0x0008 /* Error condition */ +# define POLLHUP 0x0010 /* Hung up */ +# define POLLNVAL 0x0020 /* Invalid request: fd not open */ +#endif + +# ifndef PHP_USE_POLL_2_EMULATION +# define PHP_USE_POLL_2_EMULATION 1 +# endif +#endif + +#define PHP_POLLREADABLE (POLLIN|POLLERR|POLLHUP) + +#ifndef PHP_USE_POLL_2_EMULATION +# define php_poll2(ufds, nfds, timeout) poll(ufds, nfds, timeout) +#endif + +/* timeval-to-timeout (for poll(2)) */ +static inline int php_tvtoto(struct timeval *timeouttv) +{ + if (timeouttv) { + return (timeouttv->tv_sec * 1000) + (timeouttv->tv_usec / 1000); + } + return -1; +} + +/* hybrid select(2)/poll(2) for a single descriptor. + * timeouttv follows same rules as select(2), but is reduced to millisecond accuracy. + * Returns 0 on timeout, -1 on error, or the event mask (ala poll(2)). + */ +static inline int php_pollfd_for(php_socket_t fd, int events, struct timeval *timeouttv) +{ + php_pollfd p; + int n; + + p.fd = fd; + p.events = events; + p.revents = 0; + + n = php_poll2(&p, 1, php_tvtoto(timeouttv)); + + if (n > 0) { + return p.revents; + } + + return n; +} + +static inline int php_pollfd_for_ms(php_socket_t fd, int events, int timeout) +{ + php_pollfd p; + int n; + + p.fd = fd; + p.events = events; + p.revents = 0; + + n = php_poll2(&p, 1, timeout); + + if (n > 0) { + return p.revents; + } + + return n; +} + +/* emit warning and suggestion for unsafe select(2) usage */ +PHPAPI void _php_emit_fd_setsize_warning(int max_fd); + +#ifdef PHP_WIN32 +/* 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 + + +#define PHP_SOCK_CHUNK_SIZE 8192 + +#ifdef HAVE_SOCKADDR_STORAGE +typedef struct sockaddr_storage php_sockaddr_storage; +#else +typedef struct { +#ifdef HAVE_SOCKADDR_SA_LEN + unsigned char ss_len; + unsigned char ss_family; +#else + unsigned short ss_family; +#endif + char info[126]; +} php_sockaddr_storage; +#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 + TSRMLS_DC); + +PHPAPI int php_network_connect_socket(php_socket_t sockfd, + const struct sockaddr *addr, + socklen_t addrlen, + int asynchronous, + struct timeval *timeout, + char **error_string, + int *error_code); + +#define php_connect_nonb(sock, addr, addrlen, timeout) \ + php_network_connect_socket((sock), (addr), (addrlen), 0, (timeout), NULL, NULL) + +PHPAPI php_socket_t php_network_bind_socket_to_local_addr(const char *host, unsigned port, + int socktype, char **error_string, int *error_code + TSRMLS_DC); + +PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen, + struct timeval *timeout, + char **error_string, + int *error_code + TSRMLS_DC); + +PHPAPI int php_network_get_sock_name(php_socket_t sock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC); + +PHPAPI int php_network_get_peer_name(php_socket_t sock, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC); + +PHPAPI void php_any_addr(int family, php_sockaddr_storage *addr, unsigned short port); +PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr); +END_EXTERN_C() + +struct _php_netstream_data_t { + php_socket_t socket; + char is_blocked; + struct timeval timeout; + char timeout_event; + size_t ownsize; +}; +typedef struct _php_netstream_data_t php_netstream_data_t; +PHPAPI extern php_stream_ops php_stream_socket_ops; +extern php_stream_ops php_stream_generic_socket_ops; +#define PHP_STREAM_IS_SOCKET (&php_stream_socket_ops) + +BEGIN_EXTERN_C() +PHPAPI php_stream *_php_stream_sock_open_from_socket(php_socket_t socket, const char *persistent_id STREAMS_DC TSRMLS_DC ); +/* open a connection to a host using php_hostconnect and return a stream */ +PHPAPI php_stream *_php_stream_sock_open_host(const char *host, unsigned short port, + int socktype, struct timeval *timeout, const char *persistent_id STREAMS_DC TSRMLS_DC); +PHPAPI void php_network_populate_name_from_sockaddr( + /* input address */ + struct sockaddr *sa, socklen_t sl, + /* output readable address */ + char **textaddr, long *textaddrlen, + /* output address */ + struct sockaddr **addr, + socklen_t *addrlen + TSRMLS_DC); + +PHPAPI int php_network_parse_network_address_with_port(const char *addr, + long addrlen, struct sockaddr *sa, socklen_t *sl TSRMLS_DC); +END_EXTERN_C() + +#define php_stream_sock_open_from_socket(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_CC TSRMLS_CC) +#define php_stream_sock_open_host(host, port, socktype, timeout, persistent) _php_stream_sock_open_host((host), (port), (socktype), (timeout), (persistent) STREAMS_CC TSRMLS_CC) + +/* {{{ memory debug */ +#define php_stream_sock_open_from_socket_rel(socket, persistent) _php_stream_sock_open_from_socket((socket), (persistent) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_sock_open_host_rel(host, port, socktype, timeout, persistent) _php_stream_sock_open_host((host), (port), (socktype), (timeout), (persistent) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_sock_open_unix_rel(path, pathlen, persistent, timeval) _php_stream_sock_open_unix((path), (pathlen), (persistent), (timeval) STREAMS_REL_CC TSRMLS_CC) + +/* }}} */ + +#endif /* _PHP_NETWORK_H */ + +/* + * Local variables: + * tab-width: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/main/php_open_temporary_file.c b/main/php_open_temporary_file.c new file mode 100644 index 0000000..b43d6a7 --- /dev/null +++ b/main/php_open_temporary_file.c @@ -0,0 +1,314 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" + +#include +#include +#include +#include + +#ifdef PHP_WIN32 +#define O_RDONLY _O_RDONLY +#include "win32/param.h" +#include "win32/winutil.h" +#elif defined(NETWARE) +#ifdef USE_WINSOCK +#include +#else +#include +#endif +#include +#else +#include +#include +#include +#include +#if HAVE_ARPA_INET_H +#include +#endif +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_FILE_H +#include +#endif + +#if !defined(P_tmpdir) +#define P_tmpdir "" +#endif + +/* {{{ php_do_open_temporary_file */ + +/* Loosely based on a tempnam() implementation by UCLA */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +static int php_do_open_temporary_file(const char *path, const char *pfx, char **opened_path_p TSRMLS_DC) +{ + char *trailing_slash; + char *opened_path; + char cwd[MAXPATHLEN]; + cwd_state new_state; + int fd = -1; +#ifndef HAVE_MKSTEMP + int open_flags = O_CREAT | O_TRUNC | O_RDWR +#ifdef PHP_WIN32 + | _O_BINARY +#endif + ; +#endif + + if (!path || !path[0]) { + return -1; + } + +#ifdef PHP_WIN32 + if (!php_win32_check_trailing_space(pfx, (const int)strlen(pfx))) { + SetLastError(ERROR_INVALID_NAME); + return -1; + } +#endif + + if (!VCWD_GETCWD(cwd, MAXPATHLEN)) { + cwd[0] = '\0'; + } + + new_state.cwd = strdup(cwd); + new_state.cwd_length = strlen(cwd); + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + free(new_state.cwd); + return -1; + } + + if (IS_SLASH(new_state.cwd[new_state.cwd_length - 1])) { + trailing_slash = ""; + } else { + trailing_slash = "/"; + } + + if (spprintf(&opened_path, 0, "%s%s%sXXXXXX", new_state.cwd, trailing_slash, pfx) >= MAXPATHLEN) { + efree(opened_path); + free(new_state.cwd); + return -1; + } + +#ifdef PHP_WIN32 + + if (GetTempFileName(new_state.cwd, pfx, 0, opened_path)) { + /* Some versions of windows set the temp file to be read-only, + * which means that opening it will fail... */ + if (VCWD_CHMOD(opened_path, 0600)) { + efree(opened_path); + free(new_state.cwd); + return -1; + } + fd = VCWD_OPEN_MODE(opened_path, open_flags, 0600); + } + +#elif defined(HAVE_MKSTEMP) + fd = mkstemp(opened_path); +#else + if (mktemp(opened_path)) { + fd = VCWD_OPEN(opened_path, open_flags); + } +#endif + + if (fd == -1 || !opened_path_p) { + efree(opened_path); + } else { + *opened_path_p = opened_path; + } + free(new_state.cwd); + return fd; +} +/* }}} */ + +/* Cache the chosen temporary directory. */ +static char* temporary_directory; + +PHPAPI void php_shutdown_temporary_directory(void) +{ + if (temporary_directory) { + free(temporary_directory); + temporary_directory = NULL; + } +} + +/* + * Determine where to place temporary files. + */ +PHPAPI const char* php_get_temporary_directory(void) +{ + /* Did we determine the temporary directory already? */ + if (temporary_directory) { + return temporary_directory; + } + +#ifdef PHP_WIN32 + /* We can't count on the environment variables TEMP or TMP, + * and so must make the Win32 API call to get the default + * directory for temporary files. Note this call checks + * the environment values TMP and TEMP (in order) first. + */ + { + char sTemp[MAX_PATH]; + DWORD len = GetTempPath(sizeof(sTemp),sTemp); + assert(0 < len); /* should *never* fail! */ + if (sTemp[len - 1] == DEFAULT_SLASH) { + temporary_directory = zend_strndup(sTemp, len - 1); + } else { + temporary_directory = zend_strndup(sTemp, len); + } + return temporary_directory; + } +#else + /* On Unix use the (usual) TMPDIR environment variable. */ + { + char* s = getenv("TMPDIR"); + if (s && *s) { + int len = strlen(s); + + if (s[len - 1] == DEFAULT_SLASH) { + temporary_directory = zend_strndup(s, len - 1); + } else { + temporary_directory = zend_strndup(s, len); + } + + return temporary_directory; + } + } +#ifdef P_tmpdir + /* Use the standard default temporary directory. */ + if (P_tmpdir) { + temporary_directory = strdup(P_tmpdir); + return temporary_directory; + } +#endif + /* Shouldn't ever(!) end up here ... last ditch default. */ + temporary_directory = strdup("/tmp"); + return temporary_directory; +#endif +} + +/* {{{ php_open_temporary_file + * + * Unlike tempnam(), the supplied dir argument takes precedence + * over the TMPDIR environment variable + * This function should do its best to return a file pointer to a newly created + * unique file, on every platform. + */ +PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, char **opened_path_p, zend_bool open_basedir_check TSRMLS_DC) +{ + int fd; + const char *temp_dir; + + if (!pfx) { + pfx = "tmp."; + } + if (opened_path_p) { + *opened_path_p = NULL; + } + + if (!dir || *dir == '\0') { +def_tmp: + temp_dir = php_get_temporary_directory(); + + if (temp_dir && *temp_dir != '\0' && (!open_basedir_check || !php_check_open_basedir(temp_dir TSRMLS_CC))) { + return php_do_open_temporary_file(temp_dir, pfx, opened_path_p TSRMLS_CC); + } else { + return -1; + } + } + + /* Try the directory given as parameter. */ + fd = php_do_open_temporary_file(dir, pfx, opened_path_p TSRMLS_CC); + if (fd == -1) { + /* Use default temporary directory. */ + goto def_tmp; + } + return fd; +} + +PHPAPI int php_open_temporary_fd(const char *dir, const char *pfx, char **opened_path_p TSRMLS_DC) +{ + return php_open_temporary_fd_ex(dir, pfx, opened_path_p, 0 TSRMLS_CC); +} + +PHPAPI FILE *php_open_temporary_file(const char *dir, const char *pfx, char **opened_path_p TSRMLS_DC) +{ + FILE *fp; + int fd = php_open_temporary_fd(dir, pfx, opened_path_p TSRMLS_CC); + + if (fd == -1) { + return NULL; + } + + fp = fdopen(fd, "r+b"); + if (fp == NULL) { + close(fd); + } + + return fp; +} +/* }}} */ + +/* + * 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_open_temporary_file.h b/main/php_open_temporary_file.h new file mode 100644 index 0000000..e7dce35 --- /dev/null +++ b/main/php_open_temporary_file.h @@ -0,0 +1,32 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_OPEN_TEMPORARY_FILE_H +#define PHP_OPEN_TEMPORARY_FILE_H + +BEGIN_EXTERN_C() +PHPAPI FILE *php_open_temporary_file(const char *dir, const char *pfx, char **opened_path_p TSRMLS_DC); +PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, char **opened_path_p, zend_bool open_basedir_check TSRMLS_DC); +PHPAPI int php_open_temporary_fd(const char *dir, const char *pfx, char **opened_path_p TSRMLS_DC); +PHPAPI const char *php_get_temporary_directory(void); +PHPAPI void php_shutdown_temporary_directory(void); +END_EXTERN_C() + +#endif /* PHP_OPEN_TEMPORARY_FILE_H */ diff --git a/main/php_output.h b/main/php_output.h new file mode 100644 index 0000000..833bdde --- /dev/null +++ b/main/php_output.h @@ -0,0 +1,279 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#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); +/* MSHUTDOWN */ +PHPAPI void php_output_shutdown(void); + +PHPAPI void php_output_register_constants(TSRMLS_D); + +/* 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); +PHP_FUNCTION(ob_end_flush); +PHP_FUNCTION(ob_end_clean); +PHP_FUNCTION(ob_get_flush); +PHP_FUNCTION(ob_get_clean); +PHP_FUNCTION(ob_get_contents); +PHP_FUNCTION(ob_get_length); +PHP_FUNCTION(ob_get_level); +PHP_FUNCTION(ob_get_status); +PHP_FUNCTION(ob_implicit_flush); +PHP_FUNCTION(ob_list_handlers); + +PHP_FUNCTION(output_add_rewrite_var); +PHP_FUNCTION(output_reset_rewrite_vars); + +#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/php_reentrancy.h b/main/php_reentrancy.h new file mode 100644 index 0000000..50f2e20 --- /dev/null +++ b/main/php_reentrancy.h @@ -0,0 +1,133 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Sascha Schumann | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_REENTRANCY_H +#define PHP_REENTRANCY_H + +#include "php.h" + +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#include + +/* currently, PHP does not check for these functions, but assumes + that they are available on all systems. */ + +#define HAVE_LOCALTIME 1 +#define HAVE_GMTIME 1 +#define HAVE_ASCTIME 1 +#define HAVE_CTIME 1 + +#if defined(PHP_IRIX_TIME_R) +#undef HAVE_ASCTIME_R +#undef HAVE_CTIME_R +#endif + +#if defined(PHP_HPUX_TIME_R) +#undef HAVE_LOCALTIME_R +#undef HAVE_ASCTIME_R +#undef HAVE_CTIME_R +#undef HAVE_GMTIME_R +#endif + +BEGIN_EXTERN_C() + +#if defined(HAVE_POSIX_READDIR_R) +#define php_readdir_r readdir_r +#else +PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry, + struct dirent **result); +#endif + +#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME) +#define PHP_NEED_REENTRANCY 1 +PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm); +#else +#define php_localtime_r localtime_r +#ifdef MISSING_LOCALTIME_R_DECL +struct tm *localtime_r(const time_t *const timep, struct tm *p_tm); +#endif +#endif + + +#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME) +#define PHP_NEED_REENTRANCY 1 +PHPAPI char *php_ctime_r(const time_t *clock, char *buf); +#else +#define php_ctime_r ctime_r +#ifdef MISSING_CTIME_R_DECL +char *ctime_r(const time_t *clock, char *buf); +#endif +#endif + + +#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME) +#define PHP_NEED_REENTRANCY 1 +PHPAPI char *php_asctime_r(const struct tm *tm, char *buf); +#else +#define php_asctime_r asctime_r +#ifdef MISSING_ASCTIME_R_DECL +char *asctime_r(const struct tm *tm, char *buf); +#endif +#endif + + +#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME) || defined(__BEOS__) +#define PHP_NEED_REENTRANCY 1 +PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm); +#else +#define php_gmtime_r gmtime_r +#ifdef MISSING_GMTIME_R_DECL +struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm); +#endif +#endif + +#if !defined(HAVE_STRTOK_R) +PHPAPI char *php_strtok_r(char *s, const char *delim, char **last); +#else +#define php_strtok_r strtok_r +#ifdef MISSING_STRTOK_R_DECL +char *strtok_r(char *s, const char *delim, char **last); +#endif +#endif + +#if !defined(HAVE_RAND_R) +PHPAPI int php_rand_r(unsigned int *seed); +#else +#define php_rand_r rand_r +#endif + +END_EXTERN_C() + +#if !defined(ZTS) +#undef PHP_NEED_REENTRANCY +#endif + +#if defined(PHP_NEED_REENTRANCY) +void reentrancy_startup(void); +void reentrancy_shutdown(void); +#else +#define reentrancy_startup() +#define reentrancy_shutdown() +#endif + +#endif diff --git a/main/php_scandir.c b/main/php_scandir.c new file mode 100644 index 0000000..9f3042e --- /dev/null +++ b/main/php_scandir.c @@ -0,0 +1,136 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Shane Caraveo | + | Ilia Alshanetsky | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_scandir.h" + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifndef HAVE_SCANDIR + +#ifdef PHP_WIN32 +#include "win32/param.h" +#include "win32/readdir.h" +#endif + +#include +#ifndef NETWARE +#include +#endif + +#endif /* HAVE_SCANDIR */ + +#ifndef HAVE_ALPHASORT + +#ifdef HAVE_STRING_H +#include +#endif + +PHPAPI int php_alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name,(*b)->d_name); +} +#endif /* HAVE_ALPHASORT */ + +#ifndef HAVE_SCANDIR +PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*selector) (const struct dirent *entry), int (*compare) (const struct dirent **a, const struct dirent **b)) +{ + DIR *dirp = NULL; + struct dirent **vector = NULL; + int vector_size = 0; + int nfiles = 0; + char entry[sizeof(struct dirent)+MAXPATHLEN]; + struct dirent *dp = (struct dirent *)&entry; + + if (namelist == NULL) { + return -1; + } + + if (!(dirp = opendir(dirname))) { + return -1; + } + + while (!php_readdir_r(dirp, (struct dirent *)entry, &dp) && dp) { + int dsize = 0; + struct dirent *newdp = NULL; + + if (selector && (*selector)(dp) == 0) { + continue; + } + + if (nfiles == vector_size) { + struct dirent **newv; + if (vector_size == 0) { + vector_size = 10; + } else { + vector_size *= 2; + } + + newv = (struct dirent **) realloc (vector, vector_size * sizeof (struct dirent *)); + if (!newv) { + return -1; + } + vector = newv; + } + + dsize = sizeof (struct dirent) + ((strlen(dp->d_name) + 1) * sizeof(char)); + newdp = (struct dirent *) malloc(dsize); + + if (newdp == NULL) { + goto fail; + } + + vector[nfiles++] = (struct dirent *) memcpy(newdp, dp, dsize); + } + + closedir(dirp); + + *namelist = vector; + + if (compare) { + qsort (*namelist, nfiles, sizeof(struct dirent *), compare); + } + + return nfiles; + +fail: + while (nfiles-- > 0) { + free(vector[nfiles]); + } + free(vector); + return -1; +} +#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/php_scandir.h b/main/php_scandir.h new file mode 100644 index 0000000..ad68e8d --- /dev/null +++ b/main/php_scandir.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Shane Caraveo | + | Ilia Alshanetsky | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_SCANDIR_H +#define PHP_SCANDIR_H + +#include + +#ifdef HAVE_SYS_DIR_H +#include +#endif + +#ifdef PHP_WIN32 +#include "config.w32.h" +#include "win32/readdir.h" +#else +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef HAVE_SCANDIR +#define php_scandir scandir +#else +PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*selector) (const struct dirent *entry), int (*compare) (const struct dirent **a, const struct dirent **b)); +#endif + +#ifdef HAVE_ALPHASORT +#define php_alphasort alphasort +#else +PHPAPI int php_alphasort(const struct dirent **a, const struct dirent **b); +#endif + +#endif /* PHP_SCANDIR_H */ diff --git a/main/php_sprintf.c b/main/php_sprintf.c new file mode 100644 index 0000000..9df43a5 --- /dev/null +++ b/main/php_sprintf.c @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Jaakko Hyvätti | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include +#include +#include "php.h" +#ifdef PHP_WIN32 +#include "config.w32.h" +#else +#include +#endif + +PHPAPI int +php_sprintf (char*s, const char* format, ...) +{ + va_list args; + int ret; + + va_start (args, format); + s[0] = '\0'; + ret = vsprintf (s, format, args); + va_end (args); + return (ret < 0) ? -1 : ret; +} + +/* + * 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 new file mode 100644 index 0000000..f9b9433 --- /dev/null +++ b/main/php_streams.h @@ -0,0 +1,611 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong (wez@thebrainroom.com) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PHP_STREAMS_H +#define PHP_STREAMS_H + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include + +BEGIN_EXTERN_C() +PHPAPI int php_file_le_stream(void); +PHPAPI int php_file_le_pstream(void); +PHPAPI int php_file_le_stream_filter(void); +END_EXTERN_C() + +/* {{{ Streams memory debugging stuff */ + +#if ZEND_DEBUG +/* these have more of a dependency on the definitions of the zend macros than + * I would prefer, but doing it this way saves loads of idefs :-/ */ +# define STREAMS_D int __php_stream_call_depth ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC +# define STREAMS_C 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC +# define STREAMS_REL_C __php_stream_call_depth + 1 ZEND_FILE_LINE_CC, \ + __php_stream_call_depth ? __zend_orig_filename : __zend_filename, \ + __php_stream_call_depth ? __zend_orig_lineno : __zend_lineno + +# define STREAMS_DC , STREAMS_D +# define STREAMS_CC , STREAMS_C +# define STREAMS_REL_CC , STREAMS_REL_C + +#else +# define STREAMS_D +# define STREAMS_C +# define STREAMS_REL_C +# define STREAMS_DC +# define STREAMS_CC +# define STREAMS_REL_CC +#endif + +/* these functions relay the file/line number information. They are depth aware, so they will pass + * the ultimate ancestor, which is useful, because there can be several layers of calls */ +#define php_stream_alloc_rel(ops, thisptr, persistent, mode) _php_stream_alloc((ops), (thisptr), (persistent), (mode) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_copy_to_mem_rel(src, buf, maxlen, persistent) _php_stream_copy_to_mem((src), (buf), (maxlen), (persistent) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_rel(filename, mode, opened, options) _php_stream_fopen((filename), (mode), (opened), (options) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_with_path_rel(filename, mode, path, opened, options) _php_stream_fopen_with_path((filename), (mode), (path), (opened), (options) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_from_fd_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd((fd), (mode), (persistent_id) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_fopen_from_file_rel(file, mode) _php_stream_fopen_from_file((file), (mode) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_from_pipe_rel(file, mode) _php_stream_fopen_from_pipe((file), (mode) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_tmpfile_rel() _php_stream_fopen_tmpfile(0 STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_fopen_temporary_file_rel(dir, pfx, opened_path) _php_stream_fopen_temporary_file((dir), (pfx), (opened_path) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_open_wrapper_rel(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_REL_CC TSRMLS_CC) +#define php_stream_open_wrapper_ex_rel(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_REL_CC TSRMLS_CC) + +#define php_stream_make_seekable_rel(origstream, newstream, flags) _php_stream_make_seekable((origstream), (newstream), (flags) STREAMS_REL_CC TSRMLS_CC) + +/* }}} */ + +/* The contents of the php_stream_ops and php_stream should only be accessed + * using the functions/macros in this header. + * If you need to get at something that doesn't have an API, + * drop me a line and we can sort out a way to do + * it properly. + * + * The only exceptions to this rule are that stream implementations can use + * the php_stream->abstract pointer to hold their context, and streams + * opened via stream_open_wrappers can use the zval ptr in + * php_stream->wrapperdata to hold meta data for php scripts to + * retrieve using file_get_wrapper_data(). */ + +typedef struct _php_stream php_stream; +typedef struct _php_stream_wrapper php_stream_wrapper; +typedef struct _php_stream_context php_stream_context; +typedef struct _php_stream_filter php_stream_filter; + +#include "streams/php_stream_context.h" +#include "streams/php_stream_filter_api.h" + +typedef struct _php_stream_statbuf { + struct stat sb; /* regular info */ + /* extended info to go here some day: content-type etc. etc. */ +} php_stream_statbuf; + +typedef struct _php_stream_dirent { + char d_name[MAXPATHLEN]; +} php_stream_dirent; + +/* operations on streams that are file-handles */ +typedef struct _php_stream_ops { + /* stdio like functions - these are mandatory! */ + size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC); + size_t (*read)(php_stream *stream, char *buf, size_t count TSRMLS_DC); + int (*close)(php_stream *stream, int close_handle TSRMLS_DC); + int (*flush)(php_stream *stream TSRMLS_DC); + + const char *label; /* label for this ops structure */ + + /* these are optional */ + int (*seek)(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC); + int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC); + int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); + int (*set_option)(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC); +} php_stream_ops; + +typedef struct _php_stream_wrapper_ops { + /* open/create a wrapped stream */ + php_stream *(*stream_opener)(php_stream_wrapper *wrapper, char *filename, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); + /* close/destroy a wrapped stream */ + int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC); + /* stat a wrapped stream */ + int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); + /* stat a URL */ + int (*url_stat)(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); + /* open a "directory" stream */ + php_stream *(*dir_opener)(php_stream_wrapper *wrapper, char *filename, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); + + const char *label; + + /* delete a file */ + int (*unlink)(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); + + /* rename a file */ + int (*rename)(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC); + + /* 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 { + php_stream_wrapper_ops *wops; /* operations the wrapper can perform */ + void *abstract; /* context for the wrapper */ + int is_url; /* so that PG(allow_url_fopen) can be respected */ + + /* support for wrappers to return (multiple) error messages to the stream opener */ + int err_count; /* unused */ + char **err_stack; /* unusued */ +}; + +#define PHP_STREAM_FLAG_NO_SEEK 1 +#define PHP_STREAM_FLAG_NO_BUFFER 2 + +#define PHP_STREAM_FLAG_EOL_UNIX 0 /* also includes DOS */ +#define PHP_STREAM_FLAG_DETECT_EOL 4 +#define PHP_STREAM_FLAG_EOL_MAC 8 + +/* set this when the stream might represent "interactive" data. + * When set, the read buffer will avoid certain operations that + * might otherwise cause the read to block for much longer than + * is strictly required. */ +#define PHP_STREAM_FLAG_AVOID_BLOCKING 16 + +#define PHP_STREAM_FLAG_NO_CLOSE 32 + +#define PHP_STREAM_FLAG_IS_DIR 64 + +#define PHP_STREAM_FLAG_NO_FCLOSE 128 + +struct _php_stream { + php_stream_ops *ops; + void *abstract; /* convenience pointer for abstraction */ + + php_stream_filter_chain readfilters, writefilters; + + php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */ + void *wrapperthis; /* convenience pointer for a instance of a wrapper */ + zval *wrapperdata; /* fgetwrapperdata retrieves this */ + + int fgetss_state; /* for fgetss to handle multiline tags */ + int is_persistent; + char mode[16]; /* "rwb" etc. ala stdio */ + int rsrc_id; /* used for auto-cleanup */ + int in_free; /* to prevent recursion during free */ + /* so we know how to clean it up correctly. This should be set to + * PHP_STREAM_FCLOSE_XXX as appropriate */ + int fclose_stdiocast; + FILE *stdiocast; /* cache this, otherwise we might leak! */ +#if ZEND_DEBUG + int __exposed; /* non-zero if exposed as a zval somewhere */ +#endif + char *orig_path; + + php_stream_context *context; + int flags; /* PHP_STREAM_FLAG_XXX */ + + /* buffer */ + off_t position; /* of underlying stream */ + unsigned char *readbuf; + size_t readbuflen; + off_t readpos; + off_t writepos; + + /* how much data to read when filling buffer */ + size_t chunk_size; + + int eof; + +#if ZEND_DEBUG + 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 */ +#define PHP_STREAM_FCLOSE_NONE 0 +#define PHP_STREAM_FCLOSE_FDOPEN 1 +#define PHP_STREAM_FCLOSE_FOPENCOOKIE 2 + +/* allocate a new stream for a particular ops */ +BEGIN_EXTERN_C() +PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, + const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC); +END_EXTERN_C() +#define php_stream_alloc(ops, thisptr, persistent_id, mode) _php_stream_alloc((ops), (thisptr), (persistent_id), (mode) STREAMS_CC TSRMLS_CC) + +#define php_stream_get_resource_id(stream) (stream)->rsrc_id +#if ZEND_DEBUG +/* use this to tell the stream that it is OK if we don't explicitly close it */ +# define php_stream_auto_cleanup(stream) { (stream)->__exposed++; } +/* use this to assign the stream to a zval and tell the stream that is + * has been exported to the engine; it will expect to be closed automatically + * when the resources are auto-destructed */ +# define php_stream_to_zval(stream, zval) { ZVAL_RESOURCE(zval, (stream)->rsrc_id); (stream)->__exposed++; } +#else +# define php_stream_auto_cleanup(stream) /* nothing */ +# define php_stream_to_zval(stream, zval) { ZVAL_RESOURCE(zval, (stream)->rsrc_id); } +#endif + +#define php_stream_from_zval(xstr, ppzval) ZEND_FETCH_RESOURCE2((xstr), php_stream *, (ppzval), -1, "stream", php_file_le_stream(), php_file_le_pstream()) +#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! */ +#define PHP_STREAM_PERSISTENT_NOT_EXIST 2 /* id does not exist */ + +#define PHP_STREAM_FREE_CALL_DTOR 1 /* call ops->close */ +#define PHP_STREAM_FREE_RELEASE_STREAM 2 /* pefree(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) + +PHPAPI int _php_stream_free(php_stream *stream, int close_options TSRMLS_DC); +#define php_stream_free(stream, close_options) _php_stream_free((stream), (close_options) TSRMLS_CC) +#define php_stream_close(stream) _php_stream_free((stream), PHP_STREAM_FREE_CLOSE TSRMLS_CC) +#define php_stream_pclose(stream) _php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT TSRMLS_CC) + +PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC); +#define php_stream_rewind(stream) _php_stream_seek((stream), 0L, SEEK_SET TSRMLS_CC) +#define php_stream_seek(stream, offset, whence) _php_stream_seek((stream), (offset), (whence) TSRMLS_CC) + +PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC); +#define php_stream_tell(stream) _php_stream_tell((stream) TSRMLS_CC) + +PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC); +#define php_stream_read(stream, buf, count) _php_stream_read((stream), (buf), (count) TSRMLS_CC) + +PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC); +#define php_stream_write_string(stream, str) _php_stream_write(stream, str, strlen(str) TSRMLS_CC) +#define php_stream_write(stream, buf, count) _php_stream_write(stream, (buf), (count) TSRMLS_CC) + +#ifdef ZTS +PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); +#else +PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3); +#endif + +/* php_stream_printf macro & function require TSRMLS_CC */ +#define php_stream_printf _php_stream_printf + +PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC); +#define php_stream_eof(stream) _php_stream_eof((stream) TSRMLS_CC) + +PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC); +#define php_stream_getc(stream) _php_stream_getc((stream) TSRMLS_CC) + +PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC); +#define php_stream_putc(stream, c) _php_stream_putc((stream), (c) TSRMLS_CC) + +PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC); +#define php_stream_flush(stream) _php_stream_flush((stream), 0 TSRMLS_CC) + +PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len TSRMLS_DC); +#define php_stream_gets(stream, buf, maxlen) _php_stream_get_line((stream), (buf), (maxlen), NULL TSRMLS_CC) + +#define php_stream_get_line(stream, buf, maxlen, retlen) _php_stream_get_line((stream), (buf), (maxlen), (retlen) TSRMLS_CC) +PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC); + +/* CAREFUL! this is equivalent to puts NOT fputs! */ +PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC); +#define php_stream_puts(stream, buf) _php_stream_puts((stream), (buf) TSRMLS_CC) + +PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC); +#define php_stream_stat(stream, ssb) _php_stream_stat((stream), (ssb) TSRMLS_CC) + +PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); +#define php_stream_stat_path(path, ssb) _php_stream_stat_path((path), 0, (ssb), NULL TSRMLS_CC) +#define php_stream_stat_path_ex(path, flags, ssb, context) _php_stream_stat_path((path), (flags), (ssb), (context) TSRMLS_CC) + +PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC); +#define php_stream_mkdir(path, mode, options, context) _php_stream_mkdir(path, mode, options, context TSRMLS_CC) + +PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC); +#define php_stream_rmdir(path, options, context) _php_stream_rmdir(path, options, context TSRMLS_CC) + +PHPAPI php_stream *_php_stream_opendir(char *path, int options, php_stream_context *context STREAMS_DC TSRMLS_DC); +#define php_stream_opendir(path, options, context) _php_stream_opendir((path), (options), (context) STREAMS_CC TSRMLS_CC) +PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC); +#define php_stream_readdir(dirstream, dirent) _php_stream_readdir((dirstream), (dirent) TSRMLS_CC) +#define php_stream_closedir(dirstream) php_stream_close((dirstream)) +#define php_stream_rewinddir(dirstream) php_stream_rewind((dirstream)) + +PHPAPI int php_stream_dirent_alphasort(const char **a, const char **b); +PHPAPI int php_stream_dirent_alphasortr(const char **a, const char **b); + +PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_stream_context *context, + int (*compare) (const char **a, const char **b) TSRMLS_DC); +#define php_stream_scandir(dirname, namelist, context, compare) _php_stream_scandir((dirname), (namelist), 0, (context), (compare) TSRMLS_CC) + +PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC); +#define php_stream_set_option(stream, option, value, ptrvalue) _php_stream_set_option((stream), (option), (value), (ptrvalue) TSRMLS_CC) + +#define php_stream_set_chunk_size(stream, size) _php_stream_set_option((stream), PHP_STREAM_OPTION_SET_CHUNK_SIZE, (size), NULL TSRMLS_CC) + +END_EXTERN_C() + + +/* Flags for mkdir method in wrapper ops */ +#define PHP_STREAM_MKDIR_RECURSIVE 1 +/* define REPORT ERRORS 8 (below) */ + +/* Flags for rmdir method in wrapper ops */ +/* define REPORT_ERRORS 8 (below) */ + +/* Flags for url_stat method in wrapper ops */ +#define PHP_STREAM_URL_STAT_LINK 1 +#define PHP_STREAM_URL_STAT_QUIET 2 + +/* change the blocking mode of stream: value == 1 => blocking, value == 0 => non-blocking. */ +#define PHP_STREAM_OPTION_BLOCKING 1 + +/* change the buffering mode of stream. value is a PHP_STREAM_BUFFER_XXXX value, ptrparam is a ptr to a size_t holding + * the required buffer size */ +#define PHP_STREAM_OPTION_READ_BUFFER 2 +#define PHP_STREAM_OPTION_WRITE_BUFFER 3 + +#define PHP_STREAM_BUFFER_NONE 0 /* unbuffered */ +#define PHP_STREAM_BUFFER_LINE 1 /* line buffered */ +#define PHP_STREAM_BUFFER_FULL 2 /* fully buffered */ + +/* set the timeout duration for reads on the stream. ptrparam is a pointer to a struct timeval * */ +#define PHP_STREAM_OPTION_READ_TIMEOUT 4 +#define PHP_STREAM_OPTION_SET_CHUNK_SIZE 5 + +/* set or release lock on a stream */ +#define PHP_STREAM_OPTION_LOCKING 6 + +/* whether or not locking is supported */ +#define PHP_STREAM_LOCK_SUPPORTED 1 + +#define php_stream_supports_lock(stream) _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, 0, (void *) PHP_STREAM_LOCK_SUPPORTED TSRMLS_CC) == 0 ? 1 : 0 +#define php_stream_lock(stream, mode) _php_stream_set_option((stream), PHP_STREAM_OPTION_LOCKING, (mode), (void *) NULL TSRMLS_CC) + +/* option code used by the php_stream_xport_XXX api */ +#define PHP_STREAM_OPTION_XPORT_API 7 /* see php_stream_transport.h */ +#define PHP_STREAM_OPTION_CRYPTO_API 8 /* see php_stream_transport.h */ +#define PHP_STREAM_OPTION_MMAP_API 9 /* see php_stream_mmap.h */ +#define PHP_STREAM_OPTION_TRUNCATE_API 10 + +#define PHP_STREAM_TRUNCATE_SUPPORTED 0 +#define PHP_STREAM_TRUNCATE_SET_SIZE 1 /* ptrparam is a pointer to a size_t */ + +#define php_stream_truncate_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SUPPORTED, NULL TSRMLS_CC) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0) + +BEGIN_EXTERN_C() +PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC); +#define php_stream_truncate_set_size(stream, size) _php_stream_truncate_set_size((stream), (size) TSRMLS_CC) +END_EXTERN_C() + +#define PHP_STREAM_OPTION_META_DATA_API 11 /* ptrparam is a zval* to which to add meta data information */ +#define php_stream_populate_meta_data(stream, zv) (_php_stream_set_option((stream), PHP_STREAM_OPTION_META_DATA_API, 0, zv TSRMLS_CC) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0) + +/* Check if the stream is still "live"; for sockets/pipes this means the socket + * is still connected; for files, this does not really have meaning */ +#define PHP_STREAM_OPTION_CHECK_LIVENESS 12 /* no parameters */ + +#define PHP_STREAM_OPTION_RETURN_OK 0 /* option set OK */ +#define PHP_STREAM_OPTION_RETURN_ERR -1 /* problem setting option */ +#define PHP_STREAM_OPTION_RETURN_NOTIMPL -2 /* underlying stream does not implement; streams can handle it instead */ + +/* copy up to maxlen bytes from src to dest. If maxlen is PHP_STREAM_COPY_ALL, + * copy until eof(src). */ +#define PHP_STREAM_COPY_ALL ((size_t)-1) + +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 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) + + +/* read all data from stream and put into a buffer. Caller must free buffer + * when done. */ +PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, + int persistent STREAMS_DC TSRMLS_DC); +#define php_stream_copy_to_mem(src, buf, maxlen, persistent) _php_stream_copy_to_mem((src), (buf), (maxlen), (persistent) STREAMS_CC TSRMLS_CC) + +/* output all data from a stream */ +PHPAPI size_t _php_stream_passthru(php_stream * src STREAMS_DC TSRMLS_DC); +#define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC TSRMLS_CC) +END_EXTERN_C() + +#include "streams/php_stream_transport.h" +#include "streams/php_stream_plain_wrapper.h" +#include "streams/php_stream_glob_wrapper.h" +#include "streams/php_stream_userspace.h" +#include "streams/php_stream_mmap.h" + +/* coerce the stream into some other form */ +/* cast as a stdio FILE * */ +#define PHP_STREAM_AS_STDIO 0 +/* cast as a POSIX fd or socketd */ +#define PHP_STREAM_AS_FD 1 +/* cast as a socketd */ +#define PHP_STREAM_AS_SOCKETD 2 +/* cast as fd/socket for select purposes */ +#define PHP_STREAM_AS_FD_FOR_SELECT 3 + +/* try really, really hard to make sure the cast happens (avoid using this flag if possible) */ +#define PHP_STREAM_CAST_TRY_HARD 0x80000000 +#define PHP_STREAM_CAST_RELEASE 0x40000000 /* stream becomes invalid on success */ +#define PHP_STREAM_CAST_INTERNAL 0x20000000 /* stream cast for internal use */ +#define PHP_STREAM_CAST_MASK (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE | PHP_STREAM_CAST_INTERNAL) +BEGIN_EXTERN_C() +PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC); +END_EXTERN_C() +/* use this to check if a stream can be cast into another form */ +#define php_stream_can_cast(stream, as) _php_stream_cast((stream), (as), NULL, 0 TSRMLS_CC) +#define php_stream_cast(stream, as, ret, show_err) _php_stream_cast((stream), (as), (ret), (show_err) TSRMLS_CC) + +/* use this to check if a stream is of a particular type: + * PHPAPI int php_stream_is(php_stream *stream, php_stream_ops *ops); */ +#define php_stream_is(stream, anops) ((stream)->ops == anops) +#define PHP_STREAM_IS_STDIO &php_stream_stdio_ops + +#define php_stream_is_persistent(stream) (stream)->is_persistent + +/* Wrappers support */ + +#define IGNORE_PATH 0x00000000 +#define USE_PATH 0x00000001 +#define IGNORE_URL 0x00000002 +#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. */ +#define STREAM_MUST_SEEK 0x00000010 +/* If you are going to end up casting the stream into a FILE* or + * a socket, pass this flag and the streams/wrappers will not use + * buffering mechanisms while reading the headers, so that HTTP + * wrapped streams will work consistently. + * If you omit this flag, streams will use buffering and should end + * up working more optimally. + * */ +#define STREAM_WILL_CAST 0x00000020 + +/* this flag applies to php_stream_locate_url_wrapper */ +#define STREAM_LOCATE_WRAPPERS_ONLY 0x00000040 + +/* this flag is only used by include/require functions */ +#define STREAM_OPEN_FOR_INCLUDE 0x00000080 + +/* this flag tells streams to ONLY open urls */ +#define STREAM_USE_URL 0x00000100 + +/* this flag is used when only the headers from HTTP request are to be fetched */ +#define STREAM_ONLY_GET_HEADERS 0x00000200 + +/* don't apply open_basedir checks */ +#define STREAM_DISABLE_OPEN_BASEDIR 0x00000400 + +/* get (or create) a persistent version of the stream */ +#define STREAM_OPEN_PERSISTENT 0x00000800 + +/* use glob stream for directory open in plain files stream */ +#define STREAM_USE_GLOB_DIR_OPEN 0x00001000 + +/* don't check allow_url_fopen and allow_url_include */ +#define STREAM_DISABLE_URL_PROTECTION 0x00002000 + +/* assume the path passed in exists and is fully expanded, avoiding syscalls */ +#define STREAM_ASSUME_REALPATH 0x00004000 + +/* Antique - no longer has meaning */ +#define IGNORE_URL_WIN 0 + +int php_init_stream_wrappers(int module_number TSRMLS_DC); +int php_shutdown_stream_wrappers(int module_number TSRMLS_DC); +void php_shutdown_stream_hashes(TSRMLS_D); +PHP_RSHUTDOWN_FUNCTION(streams); + +BEGIN_EXTERN_C() +PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC); +PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC); +PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC); +PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC); +PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); +PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC); +PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC); + +#define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC TSRMLS_CC) +#define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC TSRMLS_CC) + +#define php_stream_get_from_zval(stream, zstream, mode, options, opened, context) \ + if (Z_TYPE_PP((zstream)) == IS_RESOURCE) { \ + php_stream_from_zval((stream), (zstream)); \ + } else (stream) = Z_TYPE_PP((zstream)) == IS_STRING ? \ + php_stream_open_wrapper_ex(Z_STRVAL_PP((zstream)), (mode), (options), (opened), (context)) : NULL + +/* pushes an error message onto the stack for a wrapper instance */ +#ifdef ZTS +PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 4, 5); +#else +PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); +#endif + +#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */ +#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */ +#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */ +#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */ +#define PHP_STREAM_NO_PREFERENCE 0 +#define PHP_STREAM_PREFER_STDIO 1 +#define PHP_STREAM_FORCE_CONVERSION 2 +/* DO NOT call this on streams that are referenced by resources! */ +PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC); +#define php_stream_make_seekable(origstream, newstream, flags) _php_stream_make_seekable((origstream), (newstream), (flags) STREAMS_CC TSRMLS_CC) + +/* Give other modules access to the url_stream_wrappers_hash and stream_filters_hash */ +PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D); +#define php_stream_get_url_stream_wrappers_hash() _php_stream_get_url_stream_wrappers_hash(TSRMLS_C) +PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void); +PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D); +#define php_get_stream_filters_hash() _php_get_stream_filters_hash(TSRMLS_C) +PHPAPI HashTable *php_get_stream_filters_hash_global(void); +extern php_stream_wrapper_ops *php_stream_user_wrapper_ops; +END_EXTERN_C() +#endif + +/* 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 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/main/php_syslog.h b/main/php_syslog.h new file mode 100644 index 0000000..e070487 --- /dev/null +++ b/main/php_syslog.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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 PHP_SYSLOG_H +#define PHP_SYSLOG_H + +#ifdef PHP_WIN32 +#include "win32/syslog.h" +#else +#include +#ifdef HAVE_SYSLOG_H +#include +#endif +#endif + +/* + * The SCO OpenServer 5 Development System (not the UDK) + * defines syslog to std_syslog. + */ + +#ifdef syslog + +#ifdef HAVE_STD_SYSLOG +#define php_syslog std_syslog +#endif + +#undef syslog + +#endif + +#ifndef php_syslog +#define php_syslog syslog +#endif + +#endif diff --git a/main/php_ticks.c b/main/php_ticks.c new file mode 100644 index 0000000..17ffb9c --- /dev/null +++ b/main/php_ticks.c @@ -0,0 +1,86 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Stig Bakken | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "php_ticks.h" + +int php_startup_ticks(TSRMLS_D) +{ + zend_llist_init(&PG(tick_functions), sizeof(void(*)(int)), NULL, 1); + return SUCCESS; +} + +void php_deactivate_ticks(TSRMLS_D) +{ + zend_llist_clean(&PG(tick_functions)); +} + +void php_shutdown_ticks(TSRMLS_D) +{ + zend_llist_destroy(&PG(tick_functions)); +} + +static int php_compare_tick_functions(void *elem1, void *elem2) +{ + void(*func1)(int); + void(*func2)(int); + memcpy(&func1, elem1, sizeof(void(*)(int))); + memcpy(&func2, elem2, sizeof(void(*)(int))); + return (func1 == func2); +} + +PHPAPI void php_add_tick_function(void (*func)(int)) +{ + TSRMLS_FETCH(); + + zend_llist_add_element(&PG(tick_functions), (void *)&func); +} + +PHPAPI void php_remove_tick_function(void (*func)(int)) +{ + TSRMLS_FETCH(); + + zend_llist_del_element(&PG(tick_functions), (void *)func, + (int(*)(void*, void*))php_compare_tick_functions); +} + +static void php_tick_iterator(void *data, void *arg TSRMLS_DC) +{ + void (*func)(int); + + memcpy(&func, data, sizeof(void(*)(int))); + func(*((int *)arg)); +} + +void php_run_ticks(int count) +{ + TSRMLS_FETCH(); + + zend_llist_apply_with_argument(&PG(tick_functions), (llist_apply_with_arg_func_t) php_tick_iterator, &count TSRMLS_CC); +} + +/* + * 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_ticks.h b/main/php_ticks.h new file mode 100644 index 0000000..cc966fa --- /dev/null +++ b/main/php_ticks.h @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Stig Bakken | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_TICKS_H +#define PHP_TICKS_H + +int php_startup_ticks(TSRMLS_D); +void php_deactivate_ticks(TSRMLS_D); +void php_shutdown_ticks(TSRMLS_D); +void php_run_ticks(int count); + +BEGIN_EXTERN_C() +PHPAPI void php_add_tick_function(void (*func)(int)); +PHPAPI void php_remove_tick_function(void (*func)(int)); +END_EXTERN_C() + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/php_variables.c b/main/php_variables.c new file mode 100644 index 0000000..cf2b623 --- /dev/null +++ b/main/php_variables.c @@ -0,0 +1,865 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include +#include "php.h" +#include "ext/standard/php_standard.h" +#include "ext/standard/credits.h" +#include "php_variables.h" +#include "php_globals.h" +#include "php_content_types.h" +#include "SAPI.h" +#include "php_logos.h" +#include "zend_globals.h" + +/* for systems that need to override reading of environment variables */ +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC); +PHPAPI void (*php_import_environment_variables)(zval *array_ptr TSRMLS_DC) = _php_import_environment_variables; + +PHPAPI void php_register_variable(char *var, char *strval, zval *track_vars_array TSRMLS_DC) +{ + php_register_variable_safe(var, strval, strlen(strval), track_vars_array TSRMLS_CC); +} + +/* binary-safe version */ +PHPAPI void php_register_variable_safe(char *var, char *strval, int str_len, zval *track_vars_array TSRMLS_DC) +{ + zval new_entry; + assert(strval != NULL); + + /* Prepare value */ + Z_STRLEN(new_entry) = str_len; + 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); +} + +PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array TSRMLS_DC) +{ + char *p = NULL; + char *ip; /* index pointer */ + 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); + } + + if (!symtable1) { + /* Nothing to do */ + zval_dtor(val); + return; + } + + + /* ignore leading spaces in the variable name */ + 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++) { + if (*p == ' ' || *p == '.') { + *p='_'; + } else if (*p == '[') { + is_array = 1; + ip = p; + *p = 0; + break; + } + } + var_len = p - var; + + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_dtor(val); + free_alloca(var_orig, use_heap); + return; + } + + /* GLOBALS hijack attempt, reject parameter */ + if (symtable1 == EG(active_symbol_table) && + var_len == sizeof("GLOBALS")-1 && + !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) { + zval_dtor(val); + free_alloca(var_orig, use_heap); + return; + } + + index = var; + index_len = var_len; + + if (is_array) { + int nest_level = 0; + while (1) { + char *index_s; + int new_idx_len = 0; + + if(++nest_level > PG(max_input_nesting_level)) { + HashTable *ht; + /* too many levels of nesting */ + + if (track_vars_array) { + ht = Z_ARRVAL_P(track_vars_array); + zend_symtable_del(ht, var, var_len + 1); + } + + zval_dtor(val); + + /* do not output the error message to the screen, + this helps us to to avoid "information disclosure" */ + 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)); + } + free_alloca(var_orig, use_heap); + return; + } + + ip++; + index_s = ip; + if (isspace(*ip)) { + ip++; + } + if (*ip==']') { + index_s = NULL; + } else { + ip = strchr(ip, ']'); + if (!ip) { + /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ + *(index_s - 1) = '_'; + + index_len = 0; + if (index) { + index_len = strlen(index); + } + goto plain_var; + return; + } + *ip = 0; + new_idx_len = strlen(index_s); + } + + if (!index) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + if (zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p) == FAILURE) { + zval_ptr_dtor(&gpc_element); + zval_dtor(val); + free_alloca(var_orig, use_heap); + return; + } + } else { + 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, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + symtable1 = Z_ARRVAL_PP(gpc_element_p); + /* ip pointed to the '[' character, now obtain the key */ + index = index_s; + index_len = new_idx_len; + + ip++; + if (*ip == '[') { + is_array = 1; + *ip = 0; + } else { + goto plain_var; + } + } + } else { +plain_var: + MAKE_STD_ZVAL(gpc_element); + gpc_element->value = val->value; + Z_TYPE_P(gpc_element) = Z_TYPE_P(val); + if (!index) { + if (zend_hash_next_index_insert(symtable1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p) == FAILURE) { + zval_ptr_dtor(&gpc_element); + } + } else { + /* + * 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 + * to have the same (plain text) cookie name for the same path and we should not overwrite + * more specific cookies with the less specific ones. + */ + if (PG(http_globals)[TRACK_VARS_COOKIE] && + symtable1 == Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) && + zend_symtable_exists(symtable1, index, index_len + 1)) { + zval_ptr_dtor(&gpc_element); + } else { + zend_symtable_update(symtable1, index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); + } + } + } + free_alloca(var_orig, use_heap); +} + +SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) +{ + char *var, *val, *e, *s, *p; + zval *array_ptr = (zval *) arg; + long count = 0; + + if (SG(request_info).post_data == NULL) { + return; + } + + s = SG(request_info).post_data; + e = s + SG(request_info).post_data_length; + + while (s < e && (p = memchr(s, '&', (e - s)))) { +last_value: + if ((val = memchr(s, '=', (p - s)))) { /* have a value */ + unsigned int val_len, new_val_len; + + if (++count > PG(max_input_vars)) { + 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)); + return; + } + var = s; + + php_url_decode(var, (val - s)); + val++; + val_len = php_url_decode(val, (p - val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(PARSE_POST, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } + s = p + 1; + } + if (s < e) { + p = e; + goto last_value; + } +} + +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) +{ + /* TODO: check .ini setting here and apply user-defined input filter */ + if(new_val_len) *new_val_len = val_len; + return 1; +} + +SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) +{ + char *res = NULL, *var, *val, *separator = NULL; + const char *c_var; + zval *array_ptr; + int free_buffer = 0; + char *strtok_buf = NULL; + long count = 0; + + switch (arg) { + case PARSE_POST: + case PARSE_GET: + case PARSE_COOKIE: + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + switch (arg) { + case PARSE_POST: + if (PG(http_globals)[TRACK_VARS_POST]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_POST]); + } + PG(http_globals)[TRACK_VARS_POST] = array_ptr; + break; + case PARSE_GET: + if (PG(http_globals)[TRACK_VARS_GET]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_GET]); + } + PG(http_globals)[TRACK_VARS_GET] = array_ptr; + break; + case PARSE_COOKIE: + if (PG(http_globals)[TRACK_VARS_COOKIE]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_COOKIE]); + } + PG(http_globals)[TRACK_VARS_COOKIE] = array_ptr; + break; + } + break; + default: + array_ptr = destArray; + break; + } + + if (arg == PARSE_POST) { + sapi_handle_post(array_ptr TSRMLS_CC); + return; + } + + if (arg == PARSE_GET) { /* GET data */ + c_var = SG(request_info).query_string; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_COOKIE) { /* Cookie data */ + c_var = SG(request_info).cookie_data; + if (c_var && *c_var) { + res = (char *) estrdup(c_var); + free_buffer = 1; + } else { + free_buffer = 0; + } + } else if (arg == PARSE_STRING) { /* String data */ + res = str; + free_buffer = 1; + } + + if (!res) { + return; + } + + switch (arg) { + case PARSE_GET: + case PARSE_STRING: + separator = (char *) estrdup(PG(arg_separator).input); + break; + case PARSE_COOKIE: + separator = ";\0"; + break; + } + + var = php_strtok_r(res, separator, &strtok_buf); + + while (var) { + val = strchr(var, '='); + + if (arg == PARSE_COOKIE) { + /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */ + while (isspace(*var)) { + var++; + } + if (var == val || *var == '\0') { + goto next_cookie; + } + } + + if (++count > PG(max_input_vars)) { + 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)); + break; + } + + if (val) { /* have a value */ + int val_len; + unsigned int new_val_len; + + *val++ = '\0'; + php_url_decode(var, strlen(var)); + val_len = php_url_decode(val, strlen(val)); + val = estrndup(val, val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } else { + int val_len; + unsigned int new_val_len; + + php_url_decode(var, strlen(var)); + val_len = 0; + val = estrndup("", val_len); + if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) { + php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC); + } + efree(val); + } +next_cookie: + var = php_strtok_r(NULL, separator, &strtok_buf); + } + + if (arg != PARSE_COOKIE) { + efree(separator); + } + + if (free_buffer) { + efree(res); + } +} + +void _php_import_environment_variables(zval *array_ptr TSRMLS_DC) +{ + char buf[128]; + char **env, *p, *t = buf; + size_t alloc_size = sizeof(buf); + unsigned long nlen; /* ptrdiff_t is not portable */ + + for (env = environ; env != NULL && *env != NULL; env++) { + p = strchr(*env, '='); + if (!p) { /* malformed entry? */ + continue; + } + nlen = p - *env; + if (nlen >= alloc_size) { + alloc_size = nlen + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + memcpy(t, *env, nlen); + t[nlen] = '\0'; + php_register_variable(t, p + 1, array_ptr TSRMLS_CC); + } + if (t != buf && t != NULL) { + efree(t); + } +} + +zend_bool php_std_auto_global_callback(char *name, uint name_len TSRMLS_DC) +{ + zend_printf("%s\n", name); + return 0; /* don't rearm */ +} + +/* {{{ php_build_argv + */ +static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC) +{ + zval *arr, *argc, *tmp; + int count = 0; + char *ss, *space; + + if (!(SG(request_info).argc || track_vars_array)) { + return; + } + + ALLOC_INIT_ZVAL(arr); + array_init(arr); + + /* Prepare argv */ + if (SG(request_info).argc) { /* are we in cli sapi? */ + int i; + for (i = 0; i < SG(request_info).argc; i++) { + ALLOC_ZVAL(tmp); + Z_TYPE_P(tmp) = IS_STRING; + Z_STRLEN_P(tmp) = strlen(SG(request_info).argv[i]); + Z_STRVAL_P(tmp) = estrndup(SG(request_info).argv[i], Z_STRLEN_P(tmp)); + INIT_PZVAL(tmp); + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + if (Z_TYPE_P(tmp) == IS_STRING) { + efree(Z_STRVAL_P(tmp)); + } + } + } + } else if (s && *s) { + ss = s; + while (ss) { + space = strchr(ss, '+'); + if (space) { + *space = '\0'; + } + /* auto-type */ + ALLOC_ZVAL(tmp); + Z_TYPE_P(tmp) = IS_STRING; + Z_STRLEN_P(tmp) = strlen(ss); + Z_STRVAL_P(tmp) = estrndup(ss, Z_STRLEN_P(tmp)); + INIT_PZVAL(tmp); + count++; + if (zend_hash_next_index_insert(Z_ARRVAL_P(arr), &tmp, sizeof(zval *), NULL) == FAILURE) { + if (Z_TYPE_P(tmp) == IS_STRING) { + efree(Z_STRVAL_P(tmp)); + } + } + if (space) { + *space = '+'; + ss = space + 1; + } else { + ss = space; + } + } + } + + /* prepare argc */ + ALLOC_INIT_ZVAL(argc); + if (SG(request_info).argc) { + Z_LVAL_P(argc) = SG(request_info).argc; + } else { + Z_LVAL_P(argc) = count; + } + Z_TYPE_P(argc) = IS_LONG; + + 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); + zend_hash_add(&EG(symbol_table), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + if (track_vars_array) { + Z_ADDREF_P(arr); + Z_ADDREF_P(argc); + zend_hash_update(Z_ARRVAL_P(track_vars_array), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL); + zend_hash_update(Z_ARRVAL_P(track_vars_array), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL); + } + zval_ptr_dtor(&arr); + zval_ptr_dtor(&argc); +} +/* }}} */ + +/* {{{ php_handle_special_queries + */ +PHPAPI int php_handle_special_queries(TSRMLS_D) +{ + if (PG(expose_php) && SG(request_info).query_string && SG(request_info).query_string[0] == '=') { + if (php_info_logos(SG(request_info).query_string + 1 TSRMLS_CC)) { + return 1; + } else if (!strcmp(SG(request_info).query_string + 1, PHP_CREDITS_GUID)) { + php_print_credits(PHP_CREDITS_ALL TSRMLS_CC); + return 1; + } + } + return 0; +} +/* }}} */ + +/* {{{ php_register_server_variables + */ +static inline void php_register_server_variables(TSRMLS_D) +{ + zval *array_ptr = NULL; + + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + if (PG(http_globals)[TRACK_VARS_SERVER]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_SERVER]); + } + PG(http_globals)[TRACK_VARS_SERVER] = array_ptr; + + /* Server variables */ + if (sapi_module.register_server_variables) { + sapi_module.register_server_variables(array_ptr TSRMLS_CC); + } + + /* PHP Authentication support */ + if (SG(request_info).auth_user) { + php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_password) { + php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, array_ptr TSRMLS_CC); + } + if (SG(request_info).auth_digest) { + php_register_variable("PHP_AUTH_DIGEST", SG(request_info).auth_digest, array_ptr TSRMLS_CC); + } + /* store request init time */ + { + 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); + } + +} +/* }}} */ + +/* {{{ php_autoglobal_merge + */ +static void php_autoglobal_merge(HashTable *dest, HashTable *src TSRMLS_DC) +{ + zval **src_entry, **dest_entry; + char *string_key; + uint string_key_len; + ulong num_key; + HashPosition pos; + int key_type; + 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) { + key_type = zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos); + if (Z_TYPE_PP(src_entry) != IS_ARRAY + || (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 (!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 { + Z_DELREF_PP(src_entry); + } + } else { + zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL); + } + } else { + SEPARATE_ZVAL(dest_entry); + php_autoglobal_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC); + } + zend_hash_move_forward_ex(src, &pos); + } +} +/* }}} */ + +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) +{ + 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; +} +/* }}} */ + +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]); + } + 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; + + 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; + } + + 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; + } + + 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(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); + + if (PG(register_argc_argv)) { + if (SG(request_info).argc) { + 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) { + 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); + zend_hash_update(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "argc", sizeof("argc"), argc, sizeof(zval *), NULL); + } + } else { + php_build_argv(SG(request_info).query_string, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC); + } + } + + } else { + zval *server_vars=NULL; + ALLOC_ZVAL(server_vars); + array_init(server_vars); + INIT_PZVAL(server_vars); + if (PG(http_globals)[TRACK_VARS_SERVER]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_SERVER]); + } + PG(http_globals)[TRACK_VARS_SERVER] = server_vars; + } + + 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]); + + return 0; /* don't rearm */ +} + +static zend_bool php_auto_globals_create_env(const char *name, uint name_len TSRMLS_DC) +{ + zval *env_vars = NULL; + ALLOC_ZVAL(env_vars); + array_init(env_vars); + INIT_PZVAL(env_vars); + if (PG(http_globals)[TRACK_VARS_ENV]) { + zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_ENV]); + } + PG(http_globals)[TRACK_VARS_ENV] = env_vars; + + if (PG(variables_order) && (strchr(PG(variables_order),'E') || strchr(PG(variables_order),'e'))) { + php_import_environment_variables(PG(http_globals)[TRACK_VARS_ENV] TSRMLS_CC); + } + + 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]); + + return 0; /* don't rearm */ +} + +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}; + char *p; + + ALLOC_ZVAL(form_variables); + array_init(form_variables); + INIT_PZVAL(form_variables); + + if (PG(request_order) != NULL) { + p = PG(request_order); + } else { + p = PG(variables_order); + } + + for (; p && *p; p++) { + switch (*p) { + case 'g': + case 'G': + if (!_gpc_flags[0]) { + php_autoglobal_merge(Z_ARRVAL_P(form_variables), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]) TSRMLS_CC); + _gpc_flags[0] = 1; + } + break; + case 'p': + case 'P': + if (!_gpc_flags[1]) { + php_autoglobal_merge(Z_ARRVAL_P(form_variables), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_POST]) TSRMLS_CC); + _gpc_flags[1] = 1; + } + break; + case 'c': + case 'C': + if (!_gpc_flags[2]) { + php_autoglobal_merge(Z_ARRVAL_P(form_variables), Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_COOKIE]) TSRMLS_CC); + _gpc_flags[2] = 1; + } + break; + } + } + + 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(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); +} + +/* + * 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_variables.h b/main/php_variables.h new file mode 100644 index 0000000..8f5e96a --- /dev/null +++ b/main/php_variables.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_VARIABLES_H +#define PHP_VARIABLES_H + +#include "php.h" +#include "SAPI.h" + +#define PARSE_POST 0 +#define PARSE_GET 1 +#define PARSE_COOKIE 2 +#define PARSE_STRING 3 +#define PARSE_ENV 4 +#define PARSE_SERVER 5 +#define PARSE_SESSION 6 + +BEGIN_EXTERN_C() +void php_startup_auto_globals(TSRMLS_D); +extern PHPAPI void (*php_import_environment_variables)(zval *array_ptr TSRMLS_DC); +PHPAPI void php_register_variable(char *var, char *val, zval *track_vars_array TSRMLS_DC); +/* binary-safe version */ +PHPAPI void php_register_variable_safe(char *var, char *val, int val_len, zval *track_vars_array TSRMLS_DC); +PHPAPI void php_register_variable_ex(char *var, zval *val, zval *track_vars_array TSRMLS_DC); + +int php_hash_environment(TSRMLS_D); +END_EXTERN_C() + +#define NUM_TRACK_VARS 6 + +#endif /* PHP_VARIABLES_H */ diff --git a/main/php_version.h b/main/php_version.h new file mode 100644 index 0000000..e23d89e --- /dev/null +++ b/main/php_version.h @@ -0,0 +1,8 @@ +/* automatically generated by configure */ +/* edit configure.in to change version number */ +#define PHP_MAJOR_VERSION 5 +#define PHP_MINOR_VERSION 4 +#define PHP_RELEASE_VERSION 13 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "5.4.13" +#define PHP_VERSION_ID 50413 diff --git a/main/reentrancy.c b/main/reentrancy.c new file mode 100644 index 0000000..7f85224 --- /dev/null +++ b/main/reentrancy.c @@ -0,0 +1,450 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Sascha Schumann | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include +#include +#include +#ifdef HAVE_DIRENT_H +#include +#endif + +#include "php_reentrancy.h" +#include "ext/standard/php_rand.h" /* for PHP_RAND_MAX */ + +enum { + LOCALTIME_R, + CTIME_R, + ASCTIME_R, + GMTIME_R, + READDIR_R, + NUMBER_OF_LOCKS +}; + +#if defined(PHP_NEED_REENTRANCY) + +#include + +static MUTEX_T reentrant_locks[NUMBER_OF_LOCKS]; + +#define local_lock(x) tsrm_mutex_lock(reentrant_locks[x]) +#define local_unlock(x) tsrm_mutex_unlock(reentrant_locks[x]) + +#else + +#define local_lock(x) +#define local_unlock(x) + +#endif + +#if defined(PHP_IRIX_TIME_R) + +#define HAVE_CTIME_R 1 +#define HAVE_ASCTIME_R 1 + +PHPAPI char *php_ctime_r(const time_t *clock, char *buf) +{ + if (ctime_r(clock, buf) == buf) + return (buf); + return (NULL); +} + +PHPAPI char *php_asctime_r(const struct tm *tm, char *buf) +{ + if (asctime_r(tm, buf) == buf) + return (buf); + return (NULL); +} + +#endif + +#if defined(PHP_HPUX_TIME_R) + +#define HAVE_LOCALTIME_R 1 +#define HAVE_CTIME_R 1 +#define HAVE_ASCTIME_R 1 +#define HAVE_GMTIME_R 1 + +PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm) +{ + if (localtime_r(timep, p_tm) == 0) + return (p_tm); + return (NULL); +} + +PHPAPI char *php_ctime_r(const time_t *clock, char *buf) +{ + if (ctime_r(clock, buf, 26) != -1) + return (buf); + return (NULL); +} + +PHPAPI char *php_asctime_r(const struct tm *tm, char *buf) +{ + if (asctime_r(tm, buf, 26) != -1) + return (buf); + return (NULL); +} + +PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm) +{ + if (gmtime_r(timep, p_tm) == 0) + return (p_tm); + return (NULL); +} + +#endif + +#if defined(__BEOS__) + +PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm) +{ + /* Modified according to LibC definition */ + if (((struct tm*)gmtime_r(timep, p_tm)) == p_tm) + return (p_tm); + return (NULL); +} + +#endif /* BEOS */ + +#if !defined(HAVE_POSIX_READDIR_R) + +PHPAPI int php_readdir_r(DIR *dirp, struct dirent *entry, + struct dirent **result) +{ +#if defined(HAVE_OLD_READDIR_R) + int ret = 0; + + /* We cannot rely on the return value of readdir_r + as it differs between various platforms + (HPUX returns 0 on success whereas Solaris returns non-zero) + */ + entry->d_name[0] = '\0'; + readdir_r(dirp, entry); + + if (entry->d_name[0] == '\0') { + *result = NULL; + ret = errno; + } else { + *result = entry; + } + return ret; +#else + struct dirent *ptr; + int ret = 0; + + local_lock(READDIR_R); + + errno = 0; + + ptr = readdir(dirp); + + if (!ptr && errno != 0) + ret = errno; + + if (ptr) + memcpy(entry, ptr, sizeof(*ptr)); + + *result = ptr; + + local_unlock(READDIR_R); + + return ret; +#endif +} + +#endif + +#if !defined(HAVE_LOCALTIME_R) && defined(HAVE_LOCALTIME) + +PHPAPI struct tm *php_localtime_r(const time_t *const timep, struct tm *p_tm) +{ + struct tm *tmp; + + local_lock(LOCALTIME_R); + + tmp = localtime(timep); + if (tmp) { + memcpy(p_tm, tmp, sizeof(struct tm)); + tmp = p_tm; + } + + local_unlock(LOCALTIME_R); + + return tmp; +} + +#endif + +#if !defined(HAVE_CTIME_R) && defined(HAVE_CTIME) + +PHPAPI char *php_ctime_r(const time_t *clock, char *buf) +{ + char *tmp; + + local_lock(CTIME_R); + + tmp = ctime(clock); + strcpy(buf, tmp); + + local_unlock(CTIME_R); + + return buf; +} + +#endif + +#if !defined(HAVE_ASCTIME_R) && defined(HAVE_ASCTIME) + +PHPAPI char *php_asctime_r(const struct tm *tm, char *buf) +{ + char *tmp; + + local_lock(ASCTIME_R); + + tmp = asctime(tm); + strcpy(buf, tmp); + + local_unlock(ASCTIME_R); + + return buf; +} + +#endif + +#if !defined(HAVE_GMTIME_R) && defined(HAVE_GMTIME) + +PHPAPI struct tm *php_gmtime_r(const time_t *const timep, struct tm *p_tm) +{ + struct tm *tmp; + + local_lock(GMTIME_R); + + tmp = gmtime(timep); + if (tmp) { + memcpy(p_tm, tmp, sizeof(struct tm)); + tmp = p_tm; + } + + local_unlock(GMTIME_R); + + return tmp; +} + +#endif + +#if defined(PHP_NEED_REENTRANCY) + +void reentrancy_startup(void) +{ + int i; + + for (i = 0; i < NUMBER_OF_LOCKS; i++) { + reentrant_locks[i] = tsrm_mutex_alloc(); + } +} + +void reentrancy_shutdown(void) +{ + int i; + + for (i = 0; i < NUMBER_OF_LOCKS; i++) { + tsrm_mutex_free(reentrant_locks[i]); + } +} + +#endif + +#ifndef HAVE_RAND_R + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Posix rand_r function added May 1999 by Wes Peters . + */ + +#include +#include + +static int +do_rand(unsigned long *ctx) +{ + return ((*ctx = *ctx * 1103515245 + 12345) % ((u_long)PHP_RAND_MAX + 1)); +} + + +PHPAPI int +php_rand_r(unsigned int *ctx) +{ + u_long val = (u_long) *ctx; + *ctx = do_rand(&val); + return (int) *ctx; +} + +#endif + + +#ifndef HAVE_STRTOK_R + +/* + * Copyright (c) 1998 Softweyr LLC. All rights reserved. + * + * strtok_r, from Berkeley strtok + * Oct 13, 1998 by Wes Peters + * + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notices, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notices, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Softweyr LLC, the + * University of California, Berkeley, and its contributors. + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE + * REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +PHPAPI char * +php_strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + if (s == NULL && (s = *last) == NULL) + { + return NULL; + } + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0; ) + { + if (c == sc) + { + goto cont; + } + } + + if (c == 0) /* no non-delimiter characters */ + { + *last = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) + { + c = *s++; + spanp = (char *)delim; + do + { + if ((sc = *spanp++) == c) + { + if (c == 0) + { + s = NULL; + } + else + { + char *w = s - 1; + *w = '\0'; + } + *last = s; + return tok; + } + } + while (sc != 0); + } + /* NOTREACHED */ +} + +#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/rfc1867.c b/main/rfc1867.c new file mode 100644 index 0000000..35a1a15 --- /dev/null +++ b/main/rfc1867.c @@ -0,0 +1,1299 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf | + | Jani Taskinen | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* + * This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/). + * + */ + +#include +#include "php.h" +#include "php_open_temporary_file.h" +#include "zend_globals.h" +#include "php_globals.h" +#include "php_variables.h" +#include "rfc1867.h" +#include "ext/standard/php_string.h" + +#define DEBUG_FILE_UPLOAD ZEND_DEBUG + +static int dummy_encoding_translation(TSRMLS_D) +{ + return 0; +} + +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); + +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; + +PHPAPI int (*php_rfc1867_callback)(unsigned int event, void *event_data, void **extra TSRMLS_DC) = NULL; + +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]") + +/* The longest anonymous name */ +#define MAX_SIZE_ANONNAME 33 + +/* Errors */ +#define UPLOAD_ERROR_OK 0 /* File upload succesful */ +#define UPLOAD_ERROR_A 1 /* Uploaded file exceeded upload_max_filesize */ +#define UPLOAD_ERROR_B 2 /* Uploaded file exceeded MAX_FILE_SIZE */ +#define UPLOAD_ERROR_C 3 /* Partially uploaded */ +#define UPLOAD_ERROR_D 4 /* No file uploaded */ +#define UPLOAD_ERROR_E 6 /* Missing /tmp or similar directory */ +#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) /* {{{ */ +{ + 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); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE", UPLOAD_ERROR_B, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL", UPLOAD_ERROR_C, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE", UPLOAD_ERROR_D, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR", UPLOAD_ERROR_E, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE", UPLOAD_ERROR_F, CONST_CS | CONST_PERSISTENT); + REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION", UPLOAD_ERROR_X, CONST_CS | CONST_PERSISTENT); +} +/* }}} */ + +static void normalize_protected_variable(char *varname TSRMLS_DC) /* {{{ */ +{ + char *s = varname, *index = NULL, *indexend = NULL, *p; + + /* overjump leading space */ + while (*s == ' ') { + s++; + } + + /* and remove it */ + if (s != varname) { + memmove(varname, s, strlen(s)+1); + } + + for (p = varname; *p && *p != '['; p++) { + switch(*p) { + case ' ': + case '.': + *p = '_'; + break; + } + } + + /* find index */ + index = strchr(varname, '['); + if (index) { + index++; + s = index; + } else { + return; + } + + /* done? */ + while (index) { + while (*index == ' ' || *index == '\r' || *index == '\n' || *index=='\t') { + index++; + } + indexend = strchr(index, ']'); + indexend = indexend ? indexend + 1 : index + strlen(index); + + if (s != index) { + memmove(s, index, strlen(index)+1); + s += indexend-index; + } else { + s = indexend; + } + + if (*s == '[') { + s++; + index = s; + } else { + index = NULL; + } + } + *s = '\0'; +} +/* }}} */ + +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) /* {{{ */ +{ + normalize_protected_variable(varname TSRMLS_CC); + return zend_hash_exists(&PG(rfc1867_protected_variables), varname, strlen(varname)+1); +} +/* }}} */ + +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) /* {{{ */ +{ + 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) /* {{{ */ +{ + safe_php_register_variable(strvar, val, strlen(val), http_post_files, override_protection TSRMLS_CC); +} +/* }}} */ + +static void register_http_post_files_variable_ex(char *var, zval *val, zval *http_post_files, zend_bool override_protection TSRMLS_DC) /* {{{ */ +{ + safe_php_register_variable_ex(var, val, http_post_files, override_protection TSRMLS_CC); +} +/* }}} */ + +static int unlink_filename(char **filename TSRMLS_DC) /* {{{ */ +{ + VCWD_UNLINK(*filename); + return 0; +} +/* }}} */ + +void destroy_uploaded_files_hash(TSRMLS_D) /* {{{ */ +{ + zend_hash_apply(SG(rfc1867_uploaded_files), (apply_func_t) unlink_filename TSRMLS_CC); + zend_hash_destroy(SG(rfc1867_uploaded_files)); + FREE_HASHTABLE(SG(rfc1867_uploaded_files)); +} +/* }}} */ + +/* {{{ Following code is based on apache_multipart_buffer.c from libapreq-0.33 package. */ + +#define FILLUNIT (1024 * 5) + +typedef struct { + + /* read buffer */ + char *buffer; + char *buf_begin; + int bufsize; + int bytes_in_buffer; + + /* boundary info */ + char *boundary; + 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 { + char *key; + char *value; +} mime_header_entry; + +/* + * Fill up the buffer with client data. + * Returns number of bytes added to buffer. + */ +static int fill_buffer(multipart_buffer *self TSRMLS_DC) +{ + int bytes_to_read, total_read = 0, actual_read = 0; + + /* shift the existing data if necessary */ + if (self->bytes_in_buffer > 0 && self->buf_begin != self->buffer) { + memmove(self->buffer, self->buf_begin, self->bytes_in_buffer); + } + + self->buf_begin = self->buffer; + + /* calculate the free space in the buffer */ + bytes_to_read = self->bufsize - self->bytes_in_buffer; + + /* read the required number of bytes */ + while (bytes_to_read > 0) { + + char *buf = self->buffer + self->bytes_in_buffer; + + actual_read = sapi_module.read_post(buf, bytes_to_read TSRMLS_CC); + + /* update the buffer length */ + if (actual_read > 0) { + self->bytes_in_buffer += actual_read; + SG(read_post_bytes) += actual_read; + total_read += actual_read; + bytes_to_read -= actual_read; + } else { + break; + } + } + + return total_read; +} + +/* eof if we are out of bytes, or if we hit the final boundary */ +static int multipart_buffer_eof(multipart_buffer *self TSRMLS_DC) +{ + if ( (self->bytes_in_buffer == 0 && fill_buffer(self TSRMLS_CC) < 1) ) { + return 1; + } else { + return 0; + } +} + +/* create new multipart_buffer structure */ +static multipart_buffer *multipart_buffer_new(char *boundary, int boundary_len TSRMLS_DC) +{ + multipart_buffer *self = (multipart_buffer *) ecalloc(1, sizeof(multipart_buffer)); + + int minsize = boundary_len + 6; + if (minsize < FILLUNIT) minsize = FILLUNIT; + + self->buffer = (char *) ecalloc(1, minsize + 1); + self->bufsize = minsize; + + spprintf(&self->boundary, 0, "--%s", boundary); + + self->boundary_next_len = spprintf(&self->boundary_next, 0, "\n--%s", boundary); + + 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; +} + +/* + * Gets the next CRLF terminated line from the input buffer. + * If it doesn't find a CRLF, and the buffer isn't completely full, returns + * NULL; otherwise, returns the beginning of the null-terminated line, + * minus the CRLF. + * + * Note that we really just look for LF terminated lines. This works + * around a bug in internet explorer for the macintosh which sends mime + * boundaries that are only LF terminated when you use an image submit + * button in a multipart/form-data form. + */ +static char *next_line(multipart_buffer *self) +{ + /* look for LF in the data */ + char* line = self->buf_begin; + char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer); + + if (ptr) { /* LF found */ + + /* terminate the string, remove CRLF */ + if ((ptr - line) > 0 && *(ptr-1) == '\r') { + *(ptr-1) = 0; + } else { + *ptr = 0; + } + + /* bump the pointer */ + self->buf_begin = ptr + 1; + self->bytes_in_buffer -= (self->buf_begin - line); + + } else { /* no LF found */ + + /* buffer isn't completely full, fail */ + if (self->bytes_in_buffer < self->bufsize) { + return NULL; + } + /* return entire buffer as a partial line */ + line[self->bufsize] = 0; + self->buf_begin = ptr; + self->bytes_in_buffer = 0; + } + + return line; +} + +/* Returns the next CRLF terminated line from the client */ +static char *get_line(multipart_buffer *self TSRMLS_DC) +{ + char* ptr = next_line(self); + + if (!ptr) { + fill_buffer(self TSRMLS_CC); + ptr = next_line(self); + } + + return ptr; +} + +/* Free header entry */ +static void php_free_hdr_entry(mime_header_entry *h) +{ + if (h->key) { + efree(h->key); + } + if (h->value) { + efree(h->value); + } +} + +/* finds a boundary */ +static int find_boundary(multipart_buffer *self, char *boundary TSRMLS_DC) +{ + char *line; + + /* loop thru lines */ + while( (line = get_line(self TSRMLS_CC)) ) + { + /* finished if we found the boundary */ + if (!strcmp(line, boundary)) { + return 1; + } + } + + /* didn't find the boundary */ + return 0; +} + +/* parse headers */ +static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header TSRMLS_DC) +{ + char *line; + mime_header_entry prev_entry, entry; + int prev_len, cur_len; + + /* didn't find boundary, abort */ + if (!find_boundary(self, self->boundary TSRMLS_CC)) { + return 0; + } + + /* get lines of text, or CRLF_CRLF */ + + while( (line = get_line(self TSRMLS_CC)) && strlen(line) > 0 ) + { + /* add header to table */ + 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, ':'); + } + + if (value) { + *value = 0; + do { value++; } while(isspace(*value)); + + entry.value = estrdup(value); + entry.key = estrdup(key); + + } else if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */ + + prev_len = strlen(prev_entry.value); + cur_len = strlen(line); + + entry.value = emalloc(prev_len + cur_len + 1); + memcpy(entry.value, prev_entry.value, prev_len); + memcpy(entry.value + prev_len, line, cur_len); + entry.value[cur_len + prev_len] = '\0'; + + entry.key = estrdup(prev_entry.key); + + zend_llist_remove_tail(header); + } else { + continue; + } + + zend_llist_add_element(header, &entry); + prev_entry = entry; + } + + return 1; +} + +static char *php_mime_get_hdr_value(zend_llist header, char *key) +{ + mime_header_entry *entry; + + if (key == NULL) { + return NULL; + } + + entry = zend_llist_get_first(&header); + while (entry) { + if (!strcasecmp(entry->key, key)) { + return entry->value; + } + entry = zend_llist_get_next(&header); + } + + return NULL; +} + +static char *php_ap_getword(const zend_encoding *encoding, char **line, char stop TSRMLS_DC) +{ + char *pos = *line, quote; + char *res; + + while (*pos && *pos != stop) { + if ((quote = *pos) == '"' || quote == '\'') { + ++pos; + while (*pos && *pos != quote) { + if (*pos == '\\' && pos[1] && pos[1] == quote) { + pos += 2; + } else { + ++pos; + } + } + if (*pos) { + ++pos; + } + } else ++pos; + } + if (*pos == '\0') { + res = estrdup(*line); + *line += strlen(*line); + return res; + } + + res = estrndup(*line, pos - *line); + + while (*pos == stop) { + ++pos; + } + + *line = pos; + return res; +} + +static char *substring_conf(char *start, int len, char quote) +{ + char *result = emalloc(len + 1); + char *resp = result; + int 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 { + *resp++ = start[i]; + } + } + + *resp = '\0'; + return result; +} + +static char *php_ap_getword_conf(const zend_encoding *encoding, char *str TSRMLS_DC) +{ + while (*str && isspace(*str)) { + ++str; + } + + if (!*str) { + return estrdup(""); + } + + if (*str == '"' || *str == '\'') { + char quote = *str; + + str++; + return substring_conf(str, strlen(str), quote); + } else { + char *strend = str; + + while (*strend && !isspace(*strend)) { + ++strend; + } + return substring_conf(str, strend - str, 0); + } +} + +static char *php_ap_basename(const zend_encoding *encoding, char *path TSRMLS_DC) +{ + char *s = strrchr(path, '\\'); + char *s2 = strrchr(path, '/'); + + if (s && s2) { + if (s > s2) { + ++s; + } else { + s = ++s2; + } + return s; + } else if (s) { + return ++s; + } else if (s2) { + return ++s2; + } + return path; +} + +/* + * Search for a string in a fixed-length byte string. + * If partial is true, partial matches are allowed at the end of the buffer. + * Returns NULL if not found, or a pointer to the start of the first match. + */ +static void *php_ap_memstr(char *haystack, int haystacklen, char *needle, int needlen, int partial) +{ + int len = haystacklen; + char *ptr = haystack; + + /* iterate through first character matches */ + while( (ptr = memchr(ptr, needle[0], len)) ) { + + /* calculate length after match */ + len = haystacklen - (ptr - (char *)haystack); + + /* done if matches up to capacity of buffer */ + if (memcmp(needle, ptr, needlen < len ? needlen : len) == 0 && (partial || len >= needlen)) { + break; + } + + /* next character */ + ptr++; len--; + } + + return ptr; +} + +/* read until a boundary condition */ +static int multipart_buffer_read(multipart_buffer *self, char *buf, int bytes, int *end TSRMLS_DC) +{ + int len, max; + char *bound; + + /* fill buffer if needed */ + if (bytes > self->bytes_in_buffer) { + fill_buffer(self TSRMLS_CC); + } + + /* look for a potential boundary match, only read data up to that point */ + if ((bound = php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 1))) { + max = bound - self->buf_begin; + if (end && php_ap_memstr(self->buf_begin, self->bytes_in_buffer, self->boundary_next, self->boundary_next_len, 0)) { + *end = 1; + } + } else { + max = self->bytes_in_buffer; + } + + /* maximum number of bytes we are reading */ + len = max < bytes-1 ? max : bytes-1; + + /* if we read any data... */ + if (len > 0) { + + /* copy the data */ + memcpy(buf, self->buf_begin, len); + buf[len] = 0; + + if (bound && len > 0 && buf[len-1] == '\r') { + buf[--len] = 0; + } + + /* update the buffer */ + self->bytes_in_buffer -= len; + self->buf_begin += len; + } + + return len; +} + +/* + XXX: this is horrible memory-usage-wise, but we only expect + to do this on small pieces of form data. +*/ +static char *multipart_buffer_read_body(multipart_buffer *self, unsigned int *len TSRMLS_DC) +{ + char buf[FILLUNIT], *out=NULL; + int total_bytes=0, read_bytes=0; + + while((read_bytes = multipart_buffer_read(self, buf, sizeof(buf), NULL TSRMLS_CC))) { + out = erealloc(out, total_bytes + read_bytes + 1); + memcpy(out + total_bytes, buf, read_bytes); + total_bytes += read_bytes; + } + + if (out) { + out[total_bytes] = '\0'; + } + *len = total_bytes; + + return out; +} +/* }}} */ + +/* + * The combined READER/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; + int boundary_len = 0, total_bytes = 0, cancel_upload = 0, is_arr_upload = 0, array_len = 0; + int max_file_size = 0, skip_upload = 0, anonindex = 0, is_anonymous; + zval *http_post_files = NULL; + HashTable *uploaded_files = NULL; + multipart_buffer *mbuff; + zval *array_ptr = (zval *) arg; + int fd = -1; + zend_llist header; + void *event_extra_data = NULL; + 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; + } + + /* Get the boundary */ + boundary = strstr(content_type_dup, "boundary"); + if (!boundary) { + int content_type_len = strlen(content_type_dup); + char *content_type_lcase = estrndup(content_type_dup, content_type_len); + + php_strtolower(content_type_lcase, content_type_len); + boundary = strstr(content_type_lcase, "boundary"); + if (boundary) { + boundary = content_type_dup + (boundary - content_type_lcase); + } + efree(content_type_lcase); + } + + if (!boundary || !(boundary = strchr(boundary, '='))) { + sapi_module.sapi_error(E_WARNING, "Missing boundary in multipart/form-data POST data"); + return; + } + + boundary++; + boundary_len = strlen(boundary); + + if (boundary[0] == '"') { + boundary++; + boundary_end = strchr(boundary, '"'); + if (!boundary_end) { + sapi_module.sapi_error(E_WARNING, "Invalid boundary in multipart/form-data POST data"); + return; + } + } else { + /* search for the end of the boundary */ + boundary_end = strpbrk(boundary, ",;"); + } + if (boundary_end) { + boundary_end[0] = '\0'; + boundary_len = boundary_end-boundary; + } + + /* Initialize the buffer */ + if (!(mbuff = multipart_buffer_new(boundary, boundary_len TSRMLS_CC))) { + sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); + return; + } + + /* Initialize $_FILES[] */ + zend_hash_init(&PG(rfc1867_protected_variables), 5, NULL, NULL, 0); + + ALLOC_HASHTABLE(uploaded_files); + zend_hash_init(uploaded_files, 5, NULL, (dtor_func_t) free_estring, 0); + SG(rfc1867_uploaded_files) = uploaded_files; + + ALLOC_ZVAL(http_post_files); + array_init(http_post_files); + INIT_PZVAL(http_post_files); + PG(http_globals)[TRACK_VARS_FILES] = http_post_files; + + zend_llist_init(&header, sizeof(mime_header_entry), (llist_dtor_func_t) php_free_hdr_entry, 0); + + if (php_rfc1867_callback != NULL) { + multipart_event_start event_start; + + event_start.content_length = SG(request_info).content_length; + if (php_rfc1867_callback(MULTIPART_EVENT_START, &event_start, &event_extra_data TSRMLS_CC) == FAILURE) { + goto fileupload_done; + } + } + + while (!multipart_buffer_eof(mbuff TSRMLS_CC)) + { + char buff[FILLUNIT]; + char *cd = NULL, *param = NULL, *filename = NULL, *tmp = NULL; + size_t blen = 0, wlen = 0; + off_t offset; + + zend_llist_clean(&header); + + if (!multipart_buffer_headers(mbuff, &header TSRMLS_CC)) { + goto fileupload_done; + } + + if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) { + char *pair = NULL; + int end = 0; + + while (isspace(*cd)) { + ++cd; + } + + while (*cd && (pair = getword(mbuff->input_encoding, &cd, ';' TSRMLS_CC))) + { + char *key = NULL, *word = pair; + + while (isspace(*cd)) { + ++cd; + } + + if (strchr(pair, '=')) { + key = getword(mbuff->input_encoding, &pair, '=' TSRMLS_CC); + + if (!strcasecmp(key, "name")) { + if (param) { + efree(param); + } + 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 = 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) { + efree(key); + } + efree(word); + } + + /* Normal form variable, safe to read all data into memory */ + if (!filename && param) { + unsigned int value_len; + char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC); + unsigned int new_val_len; /* Dummy variable */ + + 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)) { + if (php_rfc1867_callback != NULL) { + multipart_event_formdata event_formdata; + size_t newlength = new_val_len; + + event_formdata.post_bytes_processed = SG(read_post_bytes); + event_formdata.name = param; + event_formdata.value = &value; + event_formdata.length = new_val_len; + event_formdata.newlength = &newlength; + if (php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC) == FAILURE) { + efree(param); + efree(value); + continue; + } + new_val_len = newlength; + } + safe_php_register_variable(param, value, new_val_len, array_ptr, 0 TSRMLS_CC); + } 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; + + event_formdata.post_bytes_processed = SG(read_post_bytes); + event_formdata.name = param; + event_formdata.value = &value; + event_formdata.length = value_len; + event_formdata.newlength = NULL; + php_rfc1867_callback(MULTIPART_EVENT_FORMDATA, &event_formdata, &event_extra_data TSRMLS_CC); + } + } + + if (!strcasecmp(param, "MAX_FILE_SIZE")) { + max_file_size = atol(value); + } + + efree(param); + efree(value); + continue; + } + + /* If file_uploads=off, skip the file part */ + if (!PG(file_uploads)) { + skip_upload = 1; + } else if (upload_cnt <= 0) { + skip_upload = 1; + sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded"); + } + + /* Return with an error if the posted data is garbled */ + if (!param && !filename) { + sapi_module.sapi_error(E_WARNING, "File Upload Mime headers garbled"); + goto fileupload_done; + } + + if (!param) { + is_anonymous = 1; + param = emalloc(MAX_SIZE_ANONNAME); + snprintf(param, MAX_SIZE_ANONNAME, "%u", anonindex++); + } else { + is_anonymous = 0; + } + + /* New Rule: never repair potential malicious user input */ + if (!skip_upload) { + long c = 0; + tmp = param; + + while (*tmp) { + if (*tmp == '[') { + c++; + } else if (*tmp == ']') { + c--; + if (tmp[1] && tmp[1] != '[') { + skip_upload = 1; + break; + } + } + if (c < 0) { + skip_upload = 1; + break; + } + tmp++; + } + /* Brackets should always be closed */ + if(c != 0) { + skip_upload = 1; + } + } + + total_bytes = cancel_upload = 0; + temp_filename = NULL; + fd = -1; + + if (!skip_upload && php_rfc1867_callback != NULL) { + multipart_event_file_start event_file_start; + + event_file_start.post_bytes_processed = SG(read_post_bytes); + event_file_start.name = param; + event_file_start.filename = &filename; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_START, &event_file_start, &event_extra_data TSRMLS_CC) == FAILURE) { + temp_filename = ""; + efree(param); + efree(filename); + continue; + } + } + + if (skip_upload) { + efree(param); + efree(filename); + continue; + } + + if (strlen(filename) == 0) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "No file uploaded"); +#endif + cancel_upload = UPLOAD_ERROR_D; + } + + 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); +#if DEBUG_FILE_UPLOAD + if (blen > 0) { +#else + /* in non-debug mode we have no problem with 0-length files */ + { +#endif + fd = php_open_temporary_fd_ex(PG(upload_tmp_dir), "php", &temp_filename, 1 TSRMLS_CC); + upload_cnt--; + if (fd == -1) { + sapi_module.sapi_error(E_WARNING, "File upload error - unable to create a temporary file"); + cancel_upload = UPLOAD_ERROR_E; + } + } + } + + while (!cancel_upload && (blen > 0)) + { + if (php_rfc1867_callback != NULL) { + multipart_event_file_data event_file_data; + + event_file_data.post_bytes_processed = SG(read_post_bytes); + event_file_data.offset = offset; + event_file_data.data = buff; + event_file_data.length = blen; + event_file_data.newlength = &blen; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_DATA, &event_file_data, &event_extra_data TSRMLS_CC) == FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + continue; + } + } + + 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 && ((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 + cancel_upload = UPLOAD_ERROR_B; + } else if (blen > 0) { + wlen = write(fd, buff, blen); + + if (wlen == -1) { + /* write failed */ +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); +#endif + cancel_upload = UPLOAD_ERROR_F; + } else if (wlen < blen) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "Only %d bytes were written, expected to write %d", wlen, blen); +#endif + cancel_upload = UPLOAD_ERROR_F; + } else { + total_bytes += wlen; + } + offset += wlen; + } + + /* read data for next iteration */ + blen = multipart_buffer_read(mbuff, buff, sizeof(buff), &end TSRMLS_CC); + } + + if (fd != -1) { /* may not be initialized if file could not be created */ + close(fd); + } + + if (!cancel_upload && !end) { +#if DEBUG_FILE_UPLOAD + sapi_module.sapi_error(E_NOTICE, "Missing mime boundary at the end of the data for file %s", strlen(filename) > 0 ? filename : ""); +#endif + cancel_upload = UPLOAD_ERROR_C; + } +#if DEBUG_FILE_UPLOAD + if (strlen(filename) > 0 && total_bytes == 0 && !cancel_upload) { + sapi_module.sapi_error(E_WARNING, "Uploaded file size 0 - file [%s=%s] not saved", param, filename); + cancel_upload = 5; + } +#endif + if (php_rfc1867_callback != NULL) { + multipart_event_file_end event_file_end; + + event_file_end.post_bytes_processed = SG(read_post_bytes); + event_file_end.temp_filename = temp_filename; + event_file_end.cancel_upload = cancel_upload; + if (php_rfc1867_callback(MULTIPART_EVENT_FILE_END, &event_file_end, &event_extra_data TSRMLS_CC) == FAILURE) { + cancel_upload = UPLOAD_ERROR_X; + } + } + + if (cancel_upload) { + if (temp_filename) { + if (cancel_upload != UPLOAD_ERROR_E) { /* file creation failed */ + unlink(temp_filename); + } + efree(temp_filename); + } + temp_filename = ""; + } else { + zend_hash_add(SG(rfc1867_uploaded_files), temp_filename, strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL); + } + + /* is_arr_upload is true when name of file upload field + * ends in [.*] + * start_arr is set to point to 1st [ */ + is_arr_upload = (start_arr = strchr(param,'[')) && (param[strlen(param)-1] == ']'); + + if (is_arr_upload) { + array_len = strlen(start_arr); + if (array_index) { + efree(array_index); + } + array_index = estrndup(start_arr + 1, array_len - 2); + } + + /* Add $foo_name */ + if (llen < strlen(param) + MAX_SIZE_OF_INDEX + 1) { + llen = strlen(param); + lbuf = (char *) safe_erealloc(lbuf, llen, 1, MAX_SIZE_OF_INDEX + 1); + llen += MAX_SIZE_OF_INDEX + 1; + } + + if (is_arr_upload) { + if (abuf) efree(abuf); + abuf = estrndup(param, strlen(param)-array_len); + snprintf(lbuf, llen, "%s_name[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_name", param); + } + + /* 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 = _basename(internal_encoding, filename TSRMLS_CC); + if (!s) { + s = filename; + } + + if (!is_anonymous) { + safe_php_register_variable(lbuf, s, strlen(s), NULL, 0 TSRMLS_CC); + } + + /* Add $foo[name] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[name][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[name]", param); + } + register_http_post_files_variable(lbuf, s, http_post_files, 0 TSRMLS_CC); + efree(filename); + s = NULL; + + /* Possible Content-Type: */ + if (cancel_upload || !(cd = php_mime_get_hdr_value(header, "Content-Type"))) { + cd = ""; + } else { + /* fix for Opera 6.01 */ + s = strchr(cd, ';'); + if (s != NULL) { + *s = '\0'; + } + } + + /* Add $foo_type */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s_type[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_type", param); + } + if (!is_anonymous) { + safe_php_register_variable(lbuf, cd, strlen(cd), NULL, 0 TSRMLS_CC); + } + + /* Add $foo[type] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[type][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[type]", param); + } + register_http_post_files_variable(lbuf, cd, http_post_files, 0 TSRMLS_CC); + + /* Restore Content-Type Header */ + if (s != NULL) { + *s = ';'; + } + s = ""; + + { + /* store temp_filename as-is (in case upload_tmp_dir + * contains escapeable characters. escape only the variable name.) */ + zval zfilename; + + /* Initialize variables */ + add_protected_variable(param TSRMLS_CC); + + /* if param is of form xxx[.*] this will cut it to xxx */ + if (!is_anonymous) { + ZVAL_STRING(&zfilename, temp_filename, 1); + safe_php_register_variable_ex(param, &zfilename, NULL, 1 TSRMLS_CC); + } + + /* Add $foo[tmp_name] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[tmp_name][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[tmp_name]", param); + } + add_protected_variable(lbuf TSRMLS_CC); + ZVAL_STRING(&zfilename, temp_filename, 1); + register_http_post_files_variable_ex(lbuf, &zfilename, http_post_files, 1 TSRMLS_CC); + } + + { + zval file_size, error_type; + + error_type.value.lval = cancel_upload; + error_type.type = IS_LONG; + + /* Add $foo[error] */ + if (cancel_upload) { + file_size.value.lval = 0; + file_size.type = IS_LONG; + } else { + file_size.value.lval = total_bytes; + file_size.type = IS_LONG; + } + + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[error][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[error]", param); + } + register_http_post_files_variable_ex(lbuf, &error_type, http_post_files, 0 TSRMLS_CC); + + /* Add $foo_size */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s_size[%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s_size", param); + } + if (!is_anonymous) { + safe_php_register_variable_ex(lbuf, &file_size, NULL, 0 TSRMLS_CC); + } + + /* Add $foo[size] */ + if (is_arr_upload) { + snprintf(lbuf, llen, "%s[size][%s]", abuf, array_index); + } else { + snprintf(lbuf, llen, "%s[size]", param); + } + register_http_post_files_variable_ex(lbuf, &file_size, http_post_files, 0 TSRMLS_CC); + } + efree(param); + } + } + +fileupload_done: + if (php_rfc1867_callback != NULL) { + multipart_event_end event_end; + + event_end.post_bytes_processed = SG(read_post_bytes); + php_rfc1867_callback(MULTIPART_EVENT_END, &event_end, &event_extra_data 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); +} +/* }}} */ + +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; +} +/* }}} */ + +/* + * 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/rfc1867.h b/main/rfc1867.h new file mode 100644 index 0000000..8a502c4 --- /dev/null +++ b/main/rfc1867.h @@ -0,0 +1,91 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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 RFC1867_H +#define RFC1867_H + +#include "SAPI.h" + +#define MULTIPART_CONTENT_TYPE "multipart/form-data" +#define MULTIPART_EVENT_START 0 +#define MULTIPART_EVENT_FORMDATA 1 +#define MULTIPART_EVENT_FILE_START 2 +#define MULTIPART_EVENT_FILE_DATA 3 +#define MULTIPART_EVENT_FILE_END 4 +#define MULTIPART_EVENT_END 5 + +typedef struct _multipart_event_start { + size_t content_length; +} multipart_event_start; + +typedef struct _multipart_event_formdata { + size_t post_bytes_processed; + char *name; + char **value; + size_t length; + size_t *newlength; +} multipart_event_formdata; + +typedef struct _multipart_event_file_start { + size_t post_bytes_processed; + char *name; + char **filename; +} multipart_event_file_start; + +typedef struct _multipart_event_file_data { + size_t post_bytes_processed; + off_t offset; + char *data; + size_t length; + size_t *newlength; +} multipart_event_file_data; + +typedef struct _multipart_event_file_end { + size_t post_bytes_processed; + char *temp_filename; + int cancel_upload; +} multipart_event_file_end; + +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/snprintf.c b/main/snprintf.c new file mode 100644 index 0000000..4514bd6 --- /dev/null +++ b/main/snprintf.c @@ -0,0 +1,1316 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +#define _GNU_SOURCE +#include "php.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_LOCALE_H +#include +#define LCONV_DECIMAL_POINT (*lconv->decimal_point) +#else +#define LCONV_DECIMAL_POINT '.' +#endif + +/* + * Copyright (c) 2002, 2006 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* {{{ */ +{ + register char *s = NULL; + char *p, *rve, c; + size_t siz; + + if (ndigit < 0) { + siz = -ndigit + 1; + } else { + siz = ndigit + 1; + } + + /* __dtoa() doesn't allocate space for 0 so we do it by hand */ + if (value == 0.0) { + *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */ + *sign = 0; + if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) { + return(NULL); + } + *rve++ = '0'; + *rve = '\0'; + if (!ndigit) { + return(s); + } + } else { + p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve); + if (*decpt == 9999) { + /* Infinity or Nan, convert to inf or nan like printf */ + *decpt = 0; + c = *p; + zend_freedtoa(p); + return strdup((c == 'I' ? "INF" : "NAN")); + } + /* Make a local copy and adjust rve to be in terms of s */ + if (pad && fmode) { + siz += *decpt; + } + if ((s = (char *)malloc(siz+1)) == NULL) { + zend_freedtoa(p); + return(NULL); + } + (void) strlcpy(s, p, siz); + rve = s + (rve - p); + zend_freedtoa(p); + } + + /* Add trailing zeros */ + if (pad) { + siz -= rve - s; + while (--siz) { + *rve++ = '0'; + } + *rve = '\0'; + } + + return(s); +} +/* }}} */ + +static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */ +{ + return(__cvt(value, ndigit, decpt, sign, 0, 1)); +} +/* }}} */ + +static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */ +{ + return(__cvt(value, ndigit, decpt, sign, 1, 1)); +} +/* }}} */ + +PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */ +{ + char *digits, *dst, *src; + int i, decpt, sign; + + digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL); + if (decpt == 9999) { + /* + * Infinity or NaN, convert to inf or nan with sign. + * We assume the buffer is at least ndigit long. + */ + snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN"); + zend_freedtoa(digits); + return (buf); + } + + dst = buf; + if (sign) { + *dst++ = '-'; + } + + if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */ + /* exponential format (e.g. 1.2345e+13) */ + if (--decpt < 0) { + sign = 1; + decpt = -decpt; + } else { + sign = 0; + } + src = digits; + *dst++ = *src++; + *dst++ = dec_point; + if (*src == '\0') { + *dst++ = '0'; + } else { + do { + *dst++ = *src++; + } while (*src != '\0'); + } + *dst++ = exponent; + if (sign) { + *dst++ = '-'; + } else { + *dst++ = '+'; + } + if (decpt < 10) { + *dst++ = '0' + decpt; + *dst = '\0'; + } else { + /* XXX - optimize */ + for (sign = decpt, i = 0; (sign /= 10) != 0; i++) + continue; + dst[i + 1] = '\0'; + while (decpt != 0) { + dst[i--] = '0' + decpt % 10; + decpt /= 10; + } + } + } else if (decpt < 0) { + /* standard format 0. */ + *dst++ = '0'; /* zero before decimal point */ + *dst++ = dec_point; + do { + *dst++ = '0'; + } while (++decpt < 0); + src = digits; + while (*src != '\0') { + *dst++ = *src++; + } + *dst = '\0'; + } else { + /* standard format */ + for (i = 0, src = digits; i < decpt; i++) { + if (*src != '\0') { + *dst++ = *src++; + } else { + *dst++ = '0'; + } + } + if (*src != '\0') { + if (src == digits) { + *dst++ = '0'; /* zero before decimal point */ + } + *dst++ = dec_point; + for (i = decpt; digits[i] != '\0'; i++) { + *dst++ = digits[i]; + } + } + *dst = '\0'; + } + zend_freedtoa(digits); + return (buf); +} +/* }}} */ + +/* {{{ Apache license */ +/* ==================================================================== + * Copyright (c) 1995-1998 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + * This code is based on, and used with the permission of, the + * SIO stdio-replacement strx_* functions by Panos Tsirigotis + * for xinetd. + */ +/* }}} */ + +#define FALSE 0 +#define TRUE 1 +#define NUL '\0' +#define INT_NULL ((int *)0) + +#define S_NULL "(null)" +#define S_NULL_LEN 6 + +#define FLOAT_DIGITS 6 +#define EXPONENT_LENGTH 10 + + +/* + * Convert num to its decimal format. + * Return value: + * - a pointer to a string containing the number (no sign) + * - len contains the length of the string + * - is_negative is set to TRUE or FALSE depending on the sign + * of the number (always set to FALSE if is_unsigned is TRUE) + * + * The caller provides a buffer for the string: that is the buf_end argument + * which is a pointer to the END of the buffer + 1 (i.e. if the buffer + * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) + */ +/* char * ap_php_conv_10() {{{ */ +char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned, + register bool_int * is_negative, char *buf_end, register int *len) +{ + register char *p = buf_end; + register u_wide_int magnitude; + + if (is_unsigned) { + magnitude = (u_wide_int) num; + *is_negative = FALSE; + } else { + *is_negative = (num < 0); + + /* + * On a 2's complement machine, negating the most negative integer + * results in a number that cannot be represented as a signed integer. + * Here is what we do to obtain the number's magnitude: + * a. add 1 to the number + * b. negate it (becomes positive) + * c. convert it to unsigned + * d. add 1 + */ + if (*is_negative) { + wide_int t = num + 1; + magnitude = ((u_wide_int) - t) + 1; + } else { + magnitude = (u_wide_int) num; + } + } + + /* + * We use a do-while loop so that we write at least 1 digit + */ + do { + register u_wide_int new_magnitude = magnitude / 10; + + *--p = (char)(magnitude - new_magnitude * 10 + '0'); + magnitude = new_magnitude; + } + while (magnitude); + + *len = buf_end - p; + return (p); +} +/* }}} */ + +/* If you change this value then also change bug24640.phpt. + * Also NDIG must be reasonable smaller than NUM_BUF_SIZE. + */ +#define NDIG 320 + + +/* + * Convert a floating point number to a string formats 'f', 'e' or 'E'. + * The result is placed in buf, and len denotes the length of the string + * The sign is returned in the is_negative argument (and is not placed + * in buf). + */ +/* PHPAPI char * php_conv_fp() {{{ */ +PHPAPI char * php_conv_fp(register char format, register double num, + boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len) +{ + register char *s = buf; + register char *p, *p_orig; + int decimal_point; + + if (precision >= NDIG - 1) { + precision = NDIG - 2; + } + + if (format == 'F') { + p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative); + } else { /* either e or E format */ + p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative); + } + + /* + * Check for Infinity and NaN + */ + if (isalpha((int)*p)) { + *len = strlen(p); + memcpy(buf, p, *len + 1); + *is_negative = FALSE; + free(p_orig); + return (buf); + } + if (format == 'F') { + if (decimal_point <= 0) { + if (num != 0 || precision > 0) { + *s++ = '0'; + if (precision > 0) { + *s++ = dec_point; + while (decimal_point++ < 0) { + *s++ = '0'; + } + } else if (add_dp) { + *s++ = dec_point; + } + } + } else { + int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0; + decimal_point -= addz; + while (decimal_point-- > 0) { + *s++ = *p++; + } + while (addz-- > 0) { + *s++ = '0'; + } + if (precision > 0 || add_dp) { + *s++ = dec_point; + } + } + } else { + *s++ = *p++; + if (precision > 0 || add_dp) { + *s++ = '.'; + } + } + + /* + * copy the rest of p, the NUL is NOT copied + */ + while (*p) { + *s++ = *p++; + } + + if (format != 'F') { + char temp[EXPONENT_LENGTH]; /* for exponent conversion */ + int t_len; + bool_int exponent_is_negative; + + *s++ = format; /* either e or E */ + decimal_point--; + if (decimal_point != 0) { + p = ap_php_conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); + *s++ = exponent_is_negative ? '-' : '+'; + + /* + * Make sure the exponent has at least 2 digits + */ + while (t_len--) { + *s++ = *p++; + } + } else { + *s++ = '+'; + *s++ = '0'; + } + } + *len = s - buf; + free(p_orig); + return (buf); +} +/* }}} */ + +/* + * Convert num to a base X number where X is a power of 2. nbits determines X. + * For example, if nbits is 3, we do base 8 conversion + * Return value: + * a pointer to a string containing the number + * + * The caller provides a buffer for the string: that is the buf_end argument + * which is a pointer to the END of the buffer + 1 (i.e. if the buffer + * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) + */ +char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) /* {{{ */ +{ + register int mask = (1 << nbits) - 1; + register char *p = buf_end; + static char low_digits[] = "0123456789abcdef"; + static char upper_digits[] = "0123456789ABCDEF"; + register char *digits = (format == 'X') ? upper_digits : low_digits; + + do { + *--p = digits[num & mask]; + num >>= nbits; + } + while (num); + + *len = buf_end - p; + return (p); +} +/* }}} */ + +/* + * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions + * + * XXX: this is a magic number; do not decrease it + * Emax = 1023 + * NDIG = 320 + * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1; + */ +#define NUM_BUF_SIZE 2048 + + +/* + * Descriptor for buffer area + */ +struct buf_area { + char *buf_end; + char *nextb; /* pointer to next byte to read/write */ +}; + +typedef struct buf_area buffy; + +/* + * The INS_CHAR macro inserts a character in the buffer and writes + * the buffer back to disk if necessary + * It uses the char pointers sp and bep: + * sp points to the next available character in the buffer + * bep points to the end-of-buffer+1 + * While using this macro, note that the nextb pointer is NOT updated. + * + * NOTE: Evaluation of the c argument should not have any side-effects + */ +#define INS_CHAR(c, sp, bep, cc) \ + { \ + if (sp < bep) \ + { \ + *sp++ = c; \ + } \ + cc++; \ + } + +#define NUM( c ) ( c - '0' ) + +#define STR_TO_DEC( str, num ) \ + num = NUM( *str++ ) ; \ + while ( isdigit((int)*str ) ) \ + { \ + num *= 10 ; \ + num += NUM( *str++ ) ; \ + } + +/* + * This macro does zero padding so that the precision + * requirement is satisfied. The padding is done by + * adding '0's to the left of the string that is going + * to be printed. + */ +#define FIX_PRECISION( adjust, precision, s, s_len ) \ + if ( adjust ) \ + while ( s_len < precision ) \ + { \ + *--s = '0' ; \ + s_len++ ; \ + } + +/* + * Macro that does padding. The padding is done by printing + * the character ch. + */ +#define PAD( width, len, ch ) do \ + { \ + INS_CHAR( ch, sp, bep, cc ) ; \ + width-- ; \ + } \ + while ( width > len ) + +/* + * Prefix the character ch to the string str + * Increase length + * Set the has_prefix flag + */ +#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES + + +/* + * Do format conversion placing the output in buffer + */ +static int format_converter(register buffy * odp, const char *fmt, va_list ap) /* {{{ */ +{ + char *sp; + char *bep; + int cc = 0; + int i; + + char *s = NULL; + int s_len, free_zcopy; + zval *zvp, zcopy; + + int min_width = 0; + int precision = 0; + enum { + LEFT, RIGHT + } adjust; + char pad_char; + char prefix_char; + + double fp_num; + wide_int i_num = (wide_int) 0; + u_wide_int ui_num; + + char num_buf[NUM_BUF_SIZE]; + char char_buf[2]; /* for printing %% and % */ + +#ifdef HAVE_LOCALE_H + struct lconv *lconv = NULL; +#endif + + /* + * Flag variables + */ + length_modifier_e modifier; + boolean_e alternate_form; + boolean_e print_sign; + boolean_e print_blank; + boolean_e adjust_precision; + boolean_e adjust_width; + bool_int is_negative; + + sp = odp->nextb; + bep = odp->buf_end; + + while (*fmt) { + if (*fmt != '%') { + INS_CHAR(*fmt, sp, bep, cc); + } else { + /* + * Default variable settings + */ + adjust = RIGHT; + alternate_form = print_sign = print_blank = NO; + pad_char = ' '; + prefix_char = NUL; + free_zcopy = 0; + + fmt++; + + /* + * Try to avoid checking for flags, width or precision + */ + if (isascii((int)*fmt) && !islower((int)*fmt)) { + /* + * Recognize flags: -, #, BLANK, + + */ + for (;; fmt++) { + if (*fmt == '-') + adjust = LEFT; + else if (*fmt == '+') + print_sign = YES; + else if (*fmt == '#') + alternate_form = YES; + else if (*fmt == ' ') + print_blank = YES; + else if (*fmt == '0') + pad_char = '0'; + else + break; + } + + /* + * Check if a width was specified + */ + if (isdigit((int)*fmt)) { + STR_TO_DEC(fmt, min_width); + adjust_width = YES; + } else if (*fmt == '*') { + min_width = va_arg(ap, int); + fmt++; + adjust_width = YES; + if (min_width < 0) { + adjust = LEFT; + min_width = -min_width; + } + } else + adjust_width = NO; + + /* + * Check if a precision was specified + */ + if (*fmt == '.') { + adjust_precision = YES; + fmt++; + if (isdigit((int)*fmt)) { + STR_TO_DEC(fmt, precision); + } else if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + if (precision < 0) + precision = 0; + } else + precision = 0; + + if (precision > FORMAT_CONV_MAX_PRECISION) { + precision = FORMAT_CONV_MAX_PRECISION; + } + } else + adjust_precision = NO; + } else + adjust_precision = adjust_width = NO; + + /* + * Modifier check + */ + switch (*fmt) { + case 'L': + fmt++; + modifier = LM_LONG_DOUBLE; + break; + case 'I': + fmt++; +#if SIZEOF_LONG_LONG + if (*fmt == '6' && *(fmt+1) == '4') { + fmt += 2; + modifier = LM_LONG_LONG; + } else +#endif + if (*fmt == '3' && *(fmt+1) == '2') { + fmt += 2; + modifier = LM_LONG; + } else { +#ifdef _WIN64 + modifier = LM_LONG_LONG; +#else + modifier = LM_LONG; +#endif + } + break; + case 'l': + fmt++; +#if SIZEOF_LONG_LONG + if (*fmt == 'l') { + fmt++; + modifier = LM_LONG_LONG; + } else +#endif + modifier = LM_LONG; + break; + case 'z': + fmt++; + modifier = LM_SIZE_T; + break; + case 'j': + fmt++; +#if SIZEOF_INTMAX_T + modifier = LM_INTMAX_T; +#else + modifier = LM_SIZE_T; +#endif + break; + case 't': + fmt++; +#if SIZEOF_PTRDIFF_T + modifier = LM_PTRDIFF_T; +#else + modifier = LM_SIZE_T; +#endif + break; + case 'h': + fmt++; + if (*fmt == 'h') { + fmt++; + } + /* these are promoted to int, so no break */ + default: + modifier = LM_STD; + break; + } + + /* + * Argument extraction and printing. + * First we determine the argument type. + * Then, we convert the argument to a string. + * On exit from the switch, s points to the string that + * must be printed, s_len has the length of the string + * The precision requirements, if any, are reflected in s_len. + * + * NOTE: pad_char may be set to '0' because of the 0 flag. + * It is reset to ' ' by non-numeric formats + */ + switch (*fmt) { + case 'Z': + zvp = (zval*) va_arg(ap, zval*); + zend_make_printable_zval(zvp, &zcopy, &free_zcopy); + if (free_zcopy) { + zvp = &zcopy; + } + s_len = Z_STRLEN_P(zvp); + s = Z_STRVAL_P(zvp); + if (adjust_precision && precision < s_len) { + s_len = precision; + } + break; + case 'u': + switch(modifier) { + default: + i_num = (wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + i_num = (wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + i_num = (wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + i_num = (wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + i_num = (wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + i_num = (wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + /* + * The rest also applies to other integer formats, so fall + * into that case. + */ + case 'd': + case 'i': + /* + * Get the arg if we haven't already. + */ + if ((*fmt) != 'u') { + switch(modifier) { + default: + i_num = (wide_int) va_arg(ap, int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + i_num = (wide_int) va_arg(ap, long int); + break; + case LM_SIZE_T: +#if SIZEOF_SSIZE_T + i_num = (wide_int) va_arg(ap, ssize_t); +#else + i_num = (wide_int) va_arg(ap, size_t); +#endif + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + i_num = (wide_int) va_arg(ap, wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + i_num = (wide_int) va_arg(ap, intmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + i_num = (wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + } + s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + + if (*fmt != 'u') { + if (is_negative) { + prefix_char = '-'; + } else if (print_sign) { + prefix_char = '+'; + } else if (print_blank) { + prefix_char = ' '; + } + } + break; + + + case 'o': + switch(modifier) { + default: + ui_num = (u_wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + ui_num = (u_wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + ui_num = (u_wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + ui_num = (u_wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && *s != '0') { + *--s = '0'; + s_len++; + } + break; + + + case 'x': + case 'X': + switch(modifier) { + default: + ui_num = (u_wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + ui_num = (u_wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + ui_num = (u_wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + ui_num = (u_wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && i_num != 0) { + *--s = *fmt; /* 'x' or 'X' */ + *--s = '0'; + s_len += 2; + } + break; + + + case 's': + case 'v': + s = va_arg(ap, char *); + if (s != NULL) { + s_len = strlen(s); + if (adjust_precision && precision < s_len) { + s_len = precision; + } + } else { + s = S_NULL; + s_len = S_NULL_LEN; + } + pad_char = ' '; + break; + + + case 'f': + case 'F': + case 'e': + case 'E': + switch(modifier) { + case LM_LONG_DOUBLE: + fp_num = (double) va_arg(ap, long double); + break; + case LM_STD: + fp_num = va_arg(ap, double); + break; + default: + goto fmt_error; + } + + if (zend_isnan(fp_num)) { + s = "NAN"; + s_len = 3; + } else if (zend_isinf(fp_num)) { + s = "INF"; + s_len = 3; + } else { +#ifdef HAVE_LOCALE_H + if (!lconv) { + lconv = localeconv(); + } +#endif + s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form, + (adjust_precision == NO) ? FLOAT_DIGITS : precision, + (*fmt == 'f')?LCONV_DECIMAL_POINT:'.', + &is_negative, &num_buf[1], &s_len); + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + } + break; + + + case 'g': + case 'k': + case 'G': + case 'H': + switch(modifier) { + case LM_LONG_DOUBLE: + fp_num = (double) va_arg(ap, long double); + break; + case LM_STD: + fp_num = va_arg(ap, double); + break; + default: + goto fmt_error; + } + + if (zend_isnan(fp_num)) { + s = "NAN"; + s_len = 3; + break; + } else if (zend_isinf(fp_num)) { + if (fp_num > 0) { + s = "INF"; + s_len = 3; + } else { + s = "-INF"; + s_len = 4; + } + break; + } + + if (adjust_precision == NO) { + precision = FLOAT_DIGITS; + } else if (precision == 0) { + precision = 1; + } + /* + * * We use &num_buf[ 1 ], so that we have room for the sign + */ +#ifdef HAVE_LOCALE_H + if (!lconv) { + lconv = localeconv(); + } +#endif + s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); + if (*s == '-') { + prefix_char = *s++; + } else if (print_sign) { + prefix_char = '+'; + } else if (print_blank) { + prefix_char = ' '; + } + + s_len = strlen(s); + + if (alternate_form && (strchr(s, '.')) == NULL) { + s[s_len++] = '.'; + } + break; + + + case 'c': + char_buf[0] = (char) (va_arg(ap, int)); + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case '%': + char_buf[0] = '%'; + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case 'n': + *(va_arg(ap, int *)) = cc; + goto skip_output; + + /* + * Always extract the argument as a "char *" pointer. We + * should be using "void *" but there are still machines + * that don't understand it. + * If the pointer size is equal to the size of an unsigned + * integer we convert the pointer to a hex number, otherwise + * we print "%p" to indicate that we don't handle "%p". + */ + case 'p': + if (sizeof(char *) <= sizeof(u_wide_int)) { + ui_num = (u_wide_int)((size_t) va_arg(ap, char *)); + s = ap_php_conv_p2(ui_num, 4, 'x', + &num_buf[NUM_BUF_SIZE], &s_len); + if (ui_num != 0) { + *--s = 'x'; + *--s = '0'; + s_len += 2; + } + } else { + s = "%p"; + s_len = 2; + } + pad_char = ' '; + break; + + + case NUL: + /* + * The last character of the format string was %. + * We ignore it. + */ + continue; + + +fmt_error: + php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt); + /* + * The default case is for unrecognized %'s. + * We print % to help the user identify what + * option is not understood. + * This is also useful in case the user wants to pass + * the output of format_converter to another function + * that understands some other % (like syslog). + * Note that we can't point s inside fmt because the + * unknown could be preceded by width etc. + */ + default: + char_buf[0] = '%'; + char_buf[1] = *fmt; + s = char_buf; + s_len = 2; + pad_char = ' '; + break; + } + + if (prefix_char != NUL) { + *--s = prefix_char; + s_len++; + } + if (adjust_width && adjust == RIGHT && min_width > s_len) { + if (pad_char == '0' && prefix_char != NUL) { + INS_CHAR(*s, sp, bep, cc) + s++; + s_len--; + min_width--; + } + PAD(min_width, s_len, pad_char); + } + /* + * Print the string s. + */ + for (i = s_len; i != 0; i--) { + INS_CHAR(*s, sp, bep, cc); + s++; + } + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(min_width, s_len, pad_char); + if (free_zcopy) { + zval_dtor(&zcopy); + } + } +skip_output: + fmt++; + } + odp->nextb = sp; + return (cc); +} +/* }}} */ + +/* + * This is the general purpose conversion function. + */ +static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */ +{ + buffy od; + int cc; + + /* + * First initialize the descriptor + * Notice that if no length is given, we initialize buf_end to the + * highest possible address. + */ + if (len == 0) { + od.buf_end = (char *) ~0; + od.nextb = (char *) ~0; + } else { + od.buf_end = &buf[len-1]; + od.nextb = buf; + } + + /* + * Do the conversion + */ + cc = format_converter(&od, format, ap); + if (len != 0 && od.nextb <= od.buf_end) { + *(od.nextb) = '\0'; + } + if (ccp) { + *ccp = cc; + } +} +/* }}} */ + +PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */ +{ + unsigned int cc; + va_list ap; + + va_start(ap, format); + strx_printv(&cc, buf, len, format, ap); + va_end(ap); + if (cc >= len) { + cc = len -1; + buf[cc] = '\0'; + } + return cc; +} +/* }}} */ + +PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */ +{ + unsigned int cc; + + strx_printv(&cc, buf, len, format, ap); + if (cc >= len) { + cc = len -1; + buf[cc] = '\0'; + } + return cc; +} +/* }}} */ + +PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */ +{ + int cc; + va_list ap; + + va_start(ap, format); + strx_printv(&cc, buf, len, format, ap); + va_end(ap); + return (cc); +} +/* }}} */ + +PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */ +{ + int cc; + + strx_printv(&cc, buf, len, format, ap); + return (cc); +} +/* }}} */ + +PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */ +{ + va_list ap2; + int cc; + + va_copy(ap2, ap); + cc = ap_php_vsnprintf(NULL, 0, format, ap2); + va_end(ap2); + + *buf = NULL; + + if (cc >= 0) { + if ((*buf = malloc(++cc)) != NULL) { + if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) { + free(*buf); + *buf = NULL; + } + } + } + + return cc; +} +/* }}} */ + +PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */ +{ + int cc; + va_list ap; + + va_start(ap, format); + cc = vasprintf(buf, format, ap); + va_end(ap); + return cc; +} +/* }}} */ + +/* + * 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/snprintf.h b/main/snprintf.h new file mode 100644 index 0000000..de03542 --- /dev/null +++ b/main/snprintf.h @@ -0,0 +1,179 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Stig Sæther Bakken | + | Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* + +Comparing: sprintf, snprintf, slprintf, spprintf + +sprintf offers the ability to make a lot of failures since it does not know + the size of the buffer it uses. Therefore usage of sprintf often + results in possible entries for buffer overrun attacks. So please + use this version only if you are sure the call is safe. sprintf + allways terminstes the buffer it writes to. + +snprintf knows the buffers size and will not write behind it. But you will + have to use either a static buffer or allocate a dynamic buffer + before beeing able to call the function. In other words you must + be sure that you really know the maximum size of the buffer required. + A bad thing is having a big maximum while in most cases you would + only need a small buffer. If the size of the resulting string is + longer or equal to the buffer size than the buffer is not terminated. + The function also returns the number of chars not including the + terminating \0 that were needed to fully comply to the print request. + +slprintf same as snprintf with the difference that it actually returns the + length printed not including the terminating \0. + +spprintf is the dynamical version of snprintf. It allocates the buffer in size + as needed and allows a maximum setting as snprintf (turn this feature + off by setting max_len to 0). spprintf is a little bit slower than + snprintf and offers possible memory leakes if you miss freeing the + buffer allocated by the function. Therfore this function should be + used where either no maximum is known or the maximum is much bigger + than normal size required. spprintf allways terminates the buffer. + +Example: + + #define MAX 1024 | #define MAX 1024 | #define MAX 1024 + char buffer[MAX] | char buffer[MAX] | char *buffer; + | | + | | // No need to initialize buffer: + | | // spprintf ignores value of buffer + sprintf(buffer, "test"); | snprintf(buffer, MAX, "test"); | spprintf(&buffer, MAX, "text"); + | | if (!buffer) + | | return OUT_OF_MEMORY + // sprintf allways terminates | // manual termination of | // spprintf allays terminates buffer + // buffer | // buffer *IS* required | + | buffer[MAX-1] = 0; | + action_with_buffer(buffer); | action_with_buffer(buffer); | action_with_buffer(buffer); + | | efree(buffer); +*/ + +#ifndef SNPRINTF_H +#define SNPRINTF_H + +typedef int bool_int; + +typedef enum { + NO = 0, YES = 1 +} boolean_e; + + +BEGIN_EXTERN_C() +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); +PHPAPI int ap_php_snprintf(char *, size_t, const char *, ...); +PHPAPI int ap_php_vsnprintf(char *, size_t, const char *, va_list ap); +PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap); +PHPAPI int ap_php_asprintf(char **buf, const char *format, ...); +PHPAPI int php_sprintf (char* s, const char* format, ...) PHP_ATTRIBUTE_FORMAT(printf, 2, 3); +PHPAPI char * php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf); +PHPAPI char * php_conv_fp(register char format, register double num, + boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len); + +END_EXTERN_C() + +#ifdef slprintf +#undef slprintf +#endif +#define slprintf ap_php_slprintf + +#ifdef vslprintf +#undef vslprintf +#endif +#define vslprintf ap_php_vslprintf + +#ifdef snprintf +#undef snprintf +#endif +#define snprintf ap_php_snprintf + +#ifdef vsnprintf +#undef vsnprintf +#endif +#define vsnprintf ap_php_vsnprintf + +#ifndef HAVE_VASPRINTF +#define vasprintf ap_php_vasprintf +#endif + +#ifndef HAVE_ASPRINTF +#define asprintf ap_php_asprintf +#endif + +#ifdef sprintf +#undef sprintf +#endif +#define sprintf php_sprintf + +typedef enum { + LM_STD = 0, +#if SIZEOF_INTMAX_T + LM_INTMAX_T, +#endif +#if SIZEOF_PTRDIFF_T + LM_PTRDIFF_T, +#endif +#if SIZEOF_LONG_LONG + LM_LONG_LONG, +#endif + LM_SIZE_T, + LM_LONG, + LM_LONG_DOUBLE +} length_modifier_e; + +#ifdef PHP_WIN32 +# define WIDE_INT __int64 +#elif SIZEOF_LONG_LONG_INT +# define WIDE_INT long long int +#elif SIZEOF_LONG_LONG +# define WIDE_INT long long +#else +# define WIDE_INT long +#endif +typedef WIDE_INT wide_int; +typedef unsigned WIDE_INT u_wide_int; + +extern char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned, + register bool_int * is_negative, char *buf_end, register int *len); + +extern char * ap_php_conv_p2(register u_wide_int num, register int nbits, + char format, char *buf_end, register int *len); + +/* The maximum precision that's allowed for float conversion. Does not include + * decimal separator, exponent, sign, terminator. Currently does not affect + * the modes e/f, only g/k/H, as those have a different limit enforced at + * another level (see NDIG in php_conv_fp()). + * Applies to the formatting functions of both spprintf.c and snprintf.c, which + * use equally sized buffers of MAX_BUF_SIZE = 512 to hold the result of the + * call to php_gcvt(). + * This should be reasonably smaller than MAX_BUF_SIZE (I think MAX_BUF_SIZE - 9 + * should be enough, but let's give some more space) */ +#define FORMAT_CONV_MAX_PRECISION 500 + +#endif /* SNPRINTF_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/spprintf.c b/main/spprintf.c new file mode 100644 index 0000000..596e1ef --- /dev/null +++ b/main/spprintf.c @@ -0,0 +1,831 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* This is the spprintf implementation. + * It has emerged from apache snprintf. See original header: + */ + +/* ==================================================================== + * Copyright (c) 1995-1998 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + * This code is based on, and used with the permission of, the + * SIO stdio-replacement strx_* functions by Panos Tsirigotis + * for xinetd. + */ +#define _GNU_SOURCE +#include "php.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef HAVE_LOCALE_H +#include +#define LCONV_DECIMAL_POINT (*lconv->decimal_point) +#else +#define LCONV_DECIMAL_POINT '.' +#endif + +#include "snprintf.h" + +#define FALSE 0 +#define TRUE 1 +#define NUL '\0' +#define INT_NULL ((int *)0) + +#define S_NULL "(null)" +#define S_NULL_LEN 6 + +#define FLOAT_DIGITS 6 +#define EXPONENT_LENGTH 10 + +#include "ext/standard/php_smart_str.h" + +/* {{{ macros */ + +/* + * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions + * + * XXX: this is a magic number; do not decrease it + * Emax = 1023 + * NDIG = 320 + * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1; + */ +#define NUM_BUF_SIZE 2048 + +/* + * The INS_CHAR macro inserts a character in the buffer. + * + * NOTE: Evaluation of the ch argument should not have any side-effects + */ +#define INS_CHAR_NR(xbuf, ch) do { \ + smart_str_appendc(xbuf, ch); \ +} while (0) + +#define INS_STRING(xbuf, s, slen) do { \ + smart_str_appendl(xbuf, s, slen); \ +} while (0) + +#define INS_CHAR(xbuf, ch) \ + INS_CHAR_NR(xbuf, ch) + +/* + * Macro that does padding. The padding is done by printing + * the character ch. + */ +#define PAD(xbuf, count, ch) do { \ + if ((count) > 0) { \ + size_t newlen; \ + smart_str_alloc(xbuf, (count), 0); \ + memset(xbuf->c + xbuf->len, ch, (count)); \ + xbuf->len += (count); \ + } \ +} while (0) + +#define NUM(c) (c - '0') + +#define STR_TO_DEC(str, num) do { \ + num = NUM(*str++); \ + while (isdigit((int)*str)) { \ + num *= 10; \ + num += NUM(*str++); \ + if (num >= INT_MAX / 10) { \ + while (isdigit((int)*str++)); \ + break; \ + } \ + } \ +} while (0) + +/* + * This macro does zero padding so that the precision + * requirement is satisfied. The padding is done by + * adding '0's to the left of the string that is going + * to be printed. + */ +#define FIX_PRECISION(adjust, precision, s, s_len) do { \ + if (adjust) \ + while (s_len < precision) { \ + *--s = '0'; \ + s_len++; \ + } \ +} while (0) + +/* }}} */ + + +#if !HAVE_STRNLEN +static size_t strnlen(const char *s, size_t maxlen) { + char *r = memchr(s, '\0', maxlen); + return r ? r-s : maxlen; +} +#endif + +/* + * Do format conversion placing the output in buffer + */ +static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap) /* {{{ */ +{ + char *s = NULL; + int s_len, free_zcopy; + zval *zvp, zcopy; + + int min_width = 0; + int precision = 0; + enum { + LEFT, RIGHT + } adjust; + char pad_char; + char prefix_char; + + double fp_num; + wide_int i_num = (wide_int) 0; + u_wide_int ui_num = (u_wide_int) 0; + + char num_buf[NUM_BUF_SIZE]; + char char_buf[2]; /* for printing %% and % */ + +#ifdef HAVE_LOCALE_H + struct lconv *lconv = NULL; +#endif + + /* + * Flag variables + */ + length_modifier_e modifier; + boolean_e alternate_form; + boolean_e print_sign; + boolean_e print_blank; + boolean_e adjust_precision; + boolean_e adjust_width; + bool_int is_negative; + + while (*fmt) { + if (*fmt != '%') { + INS_CHAR(xbuf, *fmt); + } else { + /* + * Default variable settings + */ + adjust = RIGHT; + alternate_form = print_sign = print_blank = NO; + pad_char = ' '; + prefix_char = NUL; + free_zcopy = 0; + + fmt++; + + /* + * Try to avoid checking for flags, width or precision + */ + if (isascii((int)*fmt) && !islower((int)*fmt)) { + /* + * Recognize flags: -, #, BLANK, + + */ + for (;; fmt++) { + if (*fmt == '-') + adjust = LEFT; + else if (*fmt == '+') + print_sign = YES; + else if (*fmt == '#') + alternate_form = YES; + else if (*fmt == ' ') + print_blank = YES; + else if (*fmt == '0') + pad_char = '0'; + else + break; + } + + /* + * Check if a width was specified + */ + if (isdigit((int)*fmt)) { + STR_TO_DEC(fmt, min_width); + adjust_width = YES; + } else if (*fmt == '*') { + min_width = va_arg(ap, int); + fmt++; + adjust_width = YES; + if (min_width < 0) { + adjust = LEFT; + min_width = -min_width; + } + } else + adjust_width = NO; + + /* + * Check if a precision was specified + */ + if (*fmt == '.') { + adjust_precision = YES; + fmt++; + if (isdigit((int)*fmt)) { + STR_TO_DEC(fmt, precision); + } else if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + if (precision < 0) + precision = 0; + } else + precision = 0; + + if (precision > FORMAT_CONV_MAX_PRECISION) { + precision = FORMAT_CONV_MAX_PRECISION; + } + } else + adjust_precision = NO; + } else + adjust_precision = adjust_width = NO; + + /* + * Modifier check + */ + switch (*fmt) { + case 'L': + fmt++; + modifier = LM_LONG_DOUBLE; + break; + case 'I': + fmt++; +#if SIZEOF_LONG_LONG + if (*fmt == '6' && *(fmt+1) == '4') { + fmt += 2; + modifier = LM_LONG_LONG; + } else +#endif + if (*fmt == '3' && *(fmt+1) == '2') { + fmt += 2; + modifier = LM_LONG; + } else { +#ifdef _WIN64 + modifier = LM_LONG_LONG; +#else + modifier = LM_LONG; +#endif + } + break; + case 'l': + fmt++; +#if SIZEOF_LONG_LONG + if (*fmt == 'l') { + fmt++; + modifier = LM_LONG_LONG; + } else +#endif + modifier = LM_LONG; + break; + case 'z': + fmt++; + modifier = LM_SIZE_T; + break; + case 'j': + fmt++; +#if SIZEOF_INTMAX_T + modifier = LM_INTMAX_T; +#else + modifier = LM_SIZE_T; +#endif + break; + case 't': + fmt++; +#if SIZEOF_PTRDIFF_T + modifier = LM_PTRDIFF_T; +#else + modifier = LM_SIZE_T; +#endif + break; + case 'h': + fmt++; + if (*fmt == 'h') { + fmt++; + } + /* these are promoted to int, so no break */ + default: + modifier = LM_STD; + break; + } + + /* + * Argument extraction and printing. + * First we determine the argument type. + * Then, we convert the argument to a string. + * On exit from the switch, s points to the string that + * must be printed, s_len has the length of the string + * The precision requirements, if any, are reflected in s_len. + * + * NOTE: pad_char may be set to '0' because of the 0 flag. + * It is reset to ' ' by non-numeric formats + */ + switch (*fmt) { + case 'Z': + zvp = (zval*) va_arg(ap, zval*); + zend_make_printable_zval(zvp, &zcopy, &free_zcopy); + if (free_zcopy) { + zvp = &zcopy; + } + s_len = Z_STRLEN_P(zvp); + s = Z_STRVAL_P(zvp); + if (adjust_precision && precision < s_len) { + s_len = precision; + } + break; + case 'u': + switch(modifier) { + default: + i_num = (wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + i_num = (wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + i_num = (wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + i_num = (wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + i_num = (wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + i_num = (wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + /* + * The rest also applies to other integer formats, so fall + * into that case. + */ + case 'd': + case 'i': + /* + * Get the arg if we haven't already. + */ + if ((*fmt) != 'u') { + switch(modifier) { + default: + i_num = (wide_int) va_arg(ap, int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + i_num = (wide_int) va_arg(ap, long int); + break; + case LM_SIZE_T: +#if SIZEOF_SSIZE_T + i_num = (wide_int) va_arg(ap, ssize_t); +#else + i_num = (wide_int) va_arg(ap, size_t); +#endif + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + i_num = (wide_int) va_arg(ap, wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + i_num = (wide_int) va_arg(ap, intmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + i_num = (wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + } + s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + + if (*fmt != 'u') { + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + } + break; + + + case 'o': + switch(modifier) { + default: + ui_num = (u_wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + ui_num = (u_wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + ui_num = (u_wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + ui_num = (u_wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + s = ap_php_conv_p2(ui_num, 3, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && *s != '0') { + *--s = '0'; + s_len++; + } + break; + + + case 'x': + case 'X': + switch(modifier) { + default: + ui_num = (u_wide_int) va_arg(ap, unsigned int); + break; + case LM_LONG_DOUBLE: + goto fmt_error; + case LM_LONG: + ui_num = (u_wide_int) va_arg(ap, unsigned long int); + break; + case LM_SIZE_T: + ui_num = (u_wide_int) va_arg(ap, size_t); + break; +#if SIZEOF_LONG_LONG + case LM_LONG_LONG: + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + break; +#endif +#if SIZEOF_INTMAX_T + case LM_INTMAX_T: + ui_num = (u_wide_int) va_arg(ap, uintmax_t); + break; +#endif +#if SIZEOF_PTRDIFF_T + case LM_PTRDIFF_T: + ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); + break; +#endif + } + s = ap_php_conv_p2(ui_num, 4, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && ui_num != 0) { + *--s = *fmt; /* 'x' or 'X' */ + *--s = '0'; + s_len += 2; + } + break; + + + case 's': + case 'v': + s = va_arg(ap, char *); + if (s != NULL) { + if (!adjust_precision) { + s_len = strlen(s); + } else { + s_len = strnlen(s, precision); + } + } else { + s = S_NULL; + s_len = S_NULL_LEN; + } + pad_char = ' '; + break; + + + case 'f': + case 'F': + case 'e': + case 'E': + switch(modifier) { + case LM_LONG_DOUBLE: + fp_num = (double) va_arg(ap, long double); + break; + case LM_STD: + fp_num = va_arg(ap, double); + break; + default: + goto fmt_error; + } + + if (zend_isnan(fp_num)) { + s = "nan"; + s_len = 3; + } else if (zend_isinf(fp_num)) { + s = "inf"; + s_len = 3; + } else { +#ifdef HAVE_LOCALE_H + if (!lconv) { + lconv = localeconv(); + } +#endif + s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form, + (adjust_precision == NO) ? FLOAT_DIGITS : precision, + (*fmt == 'f')?LCONV_DECIMAL_POINT:'.', + &is_negative, &num_buf[1], &s_len); + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + } + break; + + + case 'g': + case 'k': + case 'G': + case 'H': + switch(modifier) { + case LM_LONG_DOUBLE: + fp_num = (double) va_arg(ap, long double); + break; + case LM_STD: + fp_num = va_arg(ap, double); + break; + default: + goto fmt_error; + } + + if (zend_isnan(fp_num)) { + s = "NAN"; + s_len = 3; + break; + } else if (zend_isinf(fp_num)) { + if (fp_num > 0) { + s = "INF"; + s_len = 3; + } else { + s = "-INF"; + s_len = 4; + } + break; + } + + if (adjust_precision == NO) + precision = FLOAT_DIGITS; + else if (precision == 0) + precision = 1; + /* + * * We use &num_buf[ 1 ], so that we have room for the sign + */ +#ifdef HAVE_LOCALE_H + if (!lconv) { + lconv = localeconv(); + } +#endif + s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); + if (*s == '-') + prefix_char = *s++; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + + s_len = strlen(s); + + if (alternate_form && (strchr(s, '.')) == NULL) + s[s_len++] = '.'; + break; + + + case 'c': + char_buf[0] = (char) (va_arg(ap, int)); + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case '%': + char_buf[0] = '%'; + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case 'n': + *(va_arg(ap, int *)) = xbuf->len; + goto skip_output; + + /* + * Always extract the argument as a "char *" pointer. We + * should be using "void *" but there are still machines + * that don't understand it. + * If the pointer size is equal to the size of an unsigned + * integer we convert the pointer to a hex number, otherwise + * we print "%p" to indicate that we don't handle "%p". + */ + case 'p': + if (sizeof(char *) <= sizeof(u_wide_int)) { + ui_num = (u_wide_int)((size_t) va_arg(ap, char *)); + s = ap_php_conv_p2(ui_num, 4, 'x', + &num_buf[NUM_BUF_SIZE], &s_len); + if (ui_num != 0) { + *--s = 'x'; + *--s = '0'; + s_len += 2; + } + } else { + s = "%p"; + s_len = 2; + } + pad_char = ' '; + break; + + + case NUL: + /* + * The last character of the format string was %. + * We ignore it. + */ + continue; + + +fmt_error: + php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt); + /* + * The default case is for unrecognized %'s. + * We print % to help the user identify what + * option is not understood. + * This is also useful in case the user wants to pass + * the output of format_converter to another function + * that understands some other % (like syslog). + * Note that we can't point s inside fmt because the + * unknown could be preceded by width etc. + */ + default: + char_buf[0] = '%'; + char_buf[1] = *fmt; + s = char_buf; + s_len = 2; + pad_char = ' '; + break; + } + + if (prefix_char != NUL) { + *--s = prefix_char; + s_len++; + } + if (adjust_width && adjust == RIGHT && min_width > s_len) { + if (pad_char == '0' && prefix_char != NUL) { + INS_CHAR(xbuf, *s); + s++; + s_len--; + min_width--; + } + PAD(xbuf, min_width - s_len, pad_char); + } + /* + * Print the string s. + */ + INS_STRING(xbuf, s, s_len); + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(xbuf, min_width - s_len, pad_char); + if (free_zcopy) { + zval_dtor(&zcopy); + } + } +skip_output: + fmt++; + } + return; +} +/* }}} */ + +/* + * This is the general purpose conversion function. + */ +PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */ +{ + smart_str xbuf = {0}; + + xbuf_format_converter(&xbuf, format, ap); + + if (max_len && xbuf.len > max_len) { + xbuf.len = max_len; + } + smart_str_0(&xbuf); + + *pbuf = xbuf.c; + + return xbuf.len; +} +/* }}} */ + +PHPAPI int spprintf(char **pbuf, size_t max_len, const char *format, ...) /* {{{ */ +{ + int cc; + va_list ap; + + va_start(ap, format); + cc = vspprintf(pbuf, max_len, format, ap); + va_end(ap); + return (cc); +} +/* }}} */ + +/* + * 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/spprintf.h b/main/spprintf.h new file mode 100644 index 0000000..397928a --- /dev/null +++ b/main/spprintf.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* + +The pbuf parameter of all spprintf version receives a pointer to the allocated +buffer. This buffer must be freed manually after usage using efree() function. +The buffer will allways be terminated by a zero character. When pbuf is NULL +the function can be used to calculate the required size of the buffer but for +that purpose snprintf is faster. When both pbuf and the return value are 0 +than you are out of memory. + +There is also snprintf: See difference explained in snprintf.h + +*/ + +#ifndef SPPRINTF_H +#define SPPRINTF_H + +#include "snprintf.h" + +BEGIN_EXTERN_C() +PHPAPI int spprintf( char **pbuf, size_t max_len, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); + +PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) PHP_ATTRIBUTE_FORMAT(printf, 3, 0); +END_EXTERN_C() + +#endif /* SNPRINTF_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/main/streams/cast.c b/main/streams/cast.c new file mode 100644 index 0000000..878b4b5 --- /dev/null +++ b/main/streams/cast.c @@ -0,0 +1,418 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define _GNU_SOURCE +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include +#include + +#include "php_streams_int.h" + +/* Under BSD, emulate fopencookie using funopen */ +#if defined(HAVE_FUNOPEN) && !defined(HAVE_FOPENCOOKIE) +typedef struct { + int (*reader)(void *, char *, int); + int (*writer)(void *, const char *, int); + fpos_t (*seeker)(void *, fpos_t, int); + int (*closer)(void *); +} COOKIE_IO_FUNCTIONS_T; + +FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs) +{ + return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer); +} +# define HAVE_FOPENCOOKIE 1 +# define PHP_EMULATE_FOPENCOOKIE 1 +# define PHP_STREAM_COOKIE_FUNCTIONS &stream_cookie_functions +#elif defined(HAVE_FOPENCOOKIE) +# define PHP_STREAM_COOKIE_FUNCTIONS stream_cookie_functions +#endif + +/* {{{ STDIO with fopencookie */ +#if defined(PHP_EMULATE_FOPENCOOKIE) +/* use our fopencookie emulation */ +static int stream_cookie_reader(void *cookie, char *buffer, int size) +{ + int ret; + TSRMLS_FETCH(); + + ret = php_stream_read((php_stream*)cookie, buffer, size); + return ret; +} + +static int stream_cookie_writer(void *cookie, const char *buffer, int size) +{ + TSRMLS_FETCH(); + + return php_stream_write((php_stream *)cookie, (char *)buffer, size); +} + +static fpos_t stream_cookie_seeker(void *cookie, off_t position, int whence) +{ + TSRMLS_FETCH(); + + return (fpos_t)php_stream_seek((php_stream *)cookie, position, whence); +} + +static int stream_cookie_closer(void *cookie) +{ + php_stream *stream = (php_stream*)cookie; + TSRMLS_FETCH(); + + /* prevent recursion */ + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + return php_stream_close(stream); +} +#elif defined(HAVE_FOPENCOOKIE) +static ssize_t stream_cookie_reader(void *cookie, char *buffer, size_t size) +{ + ssize_t ret; + TSRMLS_FETCH(); + + ret = php_stream_read(((php_stream *)cookie), buffer, size); + return ret; +} + +static ssize_t stream_cookie_writer(void *cookie, const char *buffer, size_t size) +{ + TSRMLS_FETCH(); + + return php_stream_write(((php_stream *)cookie), (char *)buffer, size); +} + +# ifdef COOKIE_SEEKER_USES_OFF64_T +static int stream_cookie_seeker(void *cookie, __off64_t *position, int whence) +{ + TSRMLS_FETCH(); + + *position = php_stream_seek((php_stream *)cookie, (off_t)*position, whence); + + if (*position == -1) { + return -1; + } + return 0; +} +# else +static int stream_cookie_seeker(void *cookie, off_t position, int whence) +{ + TSRMLS_FETCH(); + + return php_stream_seek((php_stream *)cookie, position, whence); +} +# endif + +static int stream_cookie_closer(void *cookie) +{ + php_stream *stream = (php_stream*)cookie; + TSRMLS_FETCH(); + + /* prevent recursion */ + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + return php_stream_close(stream); +} +#endif /* elif defined(HAVE_FOPENCOOKIE) */ + +#if HAVE_FOPENCOOKIE +static COOKIE_IO_FUNCTIONS_T stream_cookie_functions = +{ + stream_cookie_reader, stream_cookie_writer, + stream_cookie_seeker, stream_cookie_closer +}; +#else +/* TODO: use socketpair() to emulate fopencookie, as suggested by Hartmut ? */ +#endif +/* }}} */ + +/* {{{ php_stream_mode_sanitize_fdopen_fopencookie + * Result should have at least size 5, e.g. to write wbx+\0 */ +void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result) +{ + /* replace modes not supported by fdopen and fopencookie, but supported + * by PHP's fread(), so that their calls won't fail */ + const char *cur_mode = stream->mode; + int has_plus = 0, + has_bin = 0, + i, + res_curs = 0; + + if (cur_mode[0] == 'r' || cur_mode[0] == 'w' || cur_mode[0] == 'a') { + result[res_curs++] = cur_mode[0]; + } else { + /* assume cur_mode[0] is 'c' or 'x'; substitute by 'w', which should not + * truncate anything in fdopen/fopencookie */ + result[res_curs++] = 'w'; + + /* x is allowed (at least by glibc & compat), but not as the 1st mode + * as in PHP and in any case is (at best) ignored by fdopen and fopencookie */ + } + + /* assume current mode has at most length 4 (e.g. wbn+) */ + for (i = 1; i < 4 && cur_mode[i] != '\0'; i++) { + if (cur_mode[i] == 'b') { + has_bin = 1; + } else if (cur_mode[i] == '+') { + has_plus = 1; + } + /* ignore 'n', 't' or other stuff */ + } + + if (has_bin) { + result[res_curs++] = 'b'; + } + if (has_plus) { + result[res_curs++] = '+'; + } + + result[res_curs] = '\0'; +} +/* }}} */ + +/* {{{ php_stream_cast */ +PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err TSRMLS_DC) +{ + int flags = castas & PHP_STREAM_CAST_MASK; + castas &= ~PHP_STREAM_CAST_MASK; + + /* synchronize our buffer (if possible) */ + if (ret && castas != PHP_STREAM_AS_FD_FOR_SELECT) { + php_stream_flush(stream); + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + off_t dummy; + + stream->ops->seek(stream, stream->position, SEEK_SET, &dummy TSRMLS_CC); + stream->readpos = stream->writepos = 0; + } + } + + /* filtered streams can only be cast as stdio, and only when fopencookie is present */ + + if (castas == PHP_STREAM_AS_STDIO) { + if (stream->stdiocast) { + if (ret) { + *(FILE**)ret = stream->stdiocast; + } + goto exit_success; + } + + /* if the stream is a stdio stream let's give it a chance to respond + * first, to avoid doubling up the layers of stdio with an fopencookie */ + if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && + stream->ops->cast && + !php_stream_is_filtered(stream) && + stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS + ) { + goto exit_success; + } + +#if HAVE_FOPENCOOKIE + /* if just checking, say yes we can be a FILE*, but don't actually create it yet */ + if (ret == NULL) { + goto exit_success; + } + + { + char fixed_mode[5]; + php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode); + *(FILE**)ret = fopencookie(stream, fixed_mode, PHP_STREAM_COOKIE_FUNCTIONS); + } + + if (*ret != NULL) { + off_t pos; + + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_FOPENCOOKIE; + + /* If the stream position is not at the start, we need to force + * the stdio layer to believe it's real location. */ + pos = php_stream_tell(stream); + if (pos > 0) { + fseek(*ret, pos, SEEK_SET); + } + + goto exit_success; + } + + /* must be either: + a) programmer error + b) no memory + -> lets bail + */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "fopencookie failed"); + return FAILURE; +#endif + + if (!php_stream_is_filtered(stream) && stream->ops->cast && stream->ops->cast(stream, castas, NULL TSRMLS_CC) == SUCCESS) { + if (FAILURE == stream->ops->cast(stream, castas, ret TSRMLS_CC)) { + return FAILURE; + } + goto exit_success; + } else if (flags & PHP_STREAM_CAST_TRY_HARD) { + php_stream *newstream; + + newstream = php_stream_fopen_tmpfile(); + if (newstream) { + int retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL); + + if (retcopy != SUCCESS) { + php_stream_close(newstream); + } else { + int retcast = php_stream_cast(newstream, castas | flags, (void **)ret, show_err); + + if (retcast == SUCCESS) { + rewind(*(FILE**)ret); + } + + /* do some specialized cleanup */ + if ((flags & PHP_STREAM_CAST_RELEASE)) { + php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); + } + + /* TODO: we probably should be setting .stdiocast and .fclose_stdiocast or + * we may be leaking the FILE*. Needs investigation, though. */ + return retcast; + } + } + } + } + + if (php_stream_is_filtered(stream)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot cast a filtered stream on this system"); + return FAILURE; + } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret TSRMLS_CC) == SUCCESS) { + goto exit_success; + } + + if (show_err) { + /* these names depend on the values of the PHP_STREAM_AS_XXX defines in php_streams.h */ + static const char *cast_names[4] = { + "STDIO FILE*", + "File Descriptor", + "Socket Descriptor", + "select()able descriptor" + }; + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); + } + + return FAILURE; + +exit_success: + + if ((stream->writepos - stream->readpos) > 0 && + stream->fclose_stdiocast != PHP_STREAM_FCLOSE_FOPENCOOKIE && + (flags & PHP_STREAM_CAST_INTERNAL) == 0 + ) { + /* the data we have buffered will be lost to the third party library that + * will be accessing the stream. Emit a warning so that the end-user will + * know that they should try something else */ + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%ld bytes of buffered data lost during stream conversion!", (long)(stream->writepos - stream->readpos)); + } + + if (castas == PHP_STREAM_AS_STDIO && ret) { + stream->stdiocast = *(FILE**)ret; + } + + if (flags & PHP_STREAM_CAST_RELEASE) { + php_stream_free(stream, PHP_STREAM_FREE_CLOSE_CASTED); + } + + return SUCCESS; + +} +/* }}} */ + +/* {{{ php_stream_open_wrapper_as_file */ +PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) +{ + FILE *fp = NULL; + php_stream *stream = NULL; + + stream = php_stream_open_wrapper_rel(path, mode, options|STREAM_WILL_CAST, opened_path); + + if (stream == NULL) { + return NULL; + } + + if (php_stream_cast(stream, PHP_STREAM_AS_STDIO|PHP_STREAM_CAST_TRY_HARD|PHP_STREAM_CAST_RELEASE, (void**)&fp, REPORT_ERRORS) == FAILURE) { + php_stream_close(stream); + if (opened_path && *opened_path) { + efree(*opened_path); + } + return NULL; + } + return fp; +} +/* }}} */ + +/* {{{ php_stream_make_seekable */ +PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC TSRMLS_DC) +{ + if (newstream == NULL) { + return PHP_STREAM_FAILED; + } + *newstream = NULL; + + if (((flags & PHP_STREAM_FORCE_CONVERSION) == 0) && origstream->ops->seek != NULL) { + *newstream = origstream; + return PHP_STREAM_UNCHANGED; + } + + /* Use a tmpfile and copy the old streams contents into it */ + + if (flags & PHP_STREAM_PREFER_STDIO) { + *newstream = php_stream_fopen_tmpfile(); + } else { + *newstream = php_stream_temp_new(); + } + + if (*newstream == NULL) { + return PHP_STREAM_FAILED; + } + +#if ZEND_DEBUG + (*newstream)->open_filename = origstream->open_filename; + (*newstream)->open_lineno = origstream->open_lineno; +#endif + + if (php_stream_copy_to_stream_ex(origstream, *newstream, PHP_STREAM_COPY_ALL, NULL) != SUCCESS) { + php_stream_close(*newstream); + *newstream = NULL; + return PHP_STREAM_CRITICAL; + } + + php_stream_close(origstream); + php_stream_seek(*newstream, 0, SEEK_SET); + + return PHP_STREAM_RELEASED; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/filter.c b/main/streams/filter.c new file mode 100644 index 0000000..6de3a92 --- /dev/null +++ b/main/streams/filter.c @@ -0,0 +1,544 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include +#include + +#include "php_streams_int.h" + +/* Global filter hash, copied to FG(stream_filters) on registration of volatile filter */ +static HashTable stream_filters_hash; + +/* Should only be used during core initialization */ +PHPAPI HashTable *php_get_stream_filters_hash_global(void) +{ + return &stream_filters_hash; +} + +/* Normal hash selection/retrieval call */ +PHPAPI HashTable *_php_get_stream_filters_hash(TSRMLS_D) +{ + return (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash); +} + +/* API for registering GLOBAL filters */ +PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC) +{ + return zend_hash_add(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL); +} + +PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC) +{ + return zend_hash_del(&stream_filters_hash, (char*)filterpattern, strlen(filterpattern) + 1); +} + +/* API for registering VOLATILE wrappers */ +PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC) +{ + if (!FG(stream_filters)) { + php_stream_filter_factory tmpfactory; + + ALLOC_HASHTABLE(FG(stream_filters)); + zend_hash_init(FG(stream_filters), zend_hash_num_elements(&stream_filters_hash), NULL, NULL, 1); + zend_hash_copy(FG(stream_filters), &stream_filters_hash, NULL, &tmpfactory, sizeof(php_stream_filter_factory)); + } + + return zend_hash_add(FG(stream_filters), (char*)filterpattern, strlen(filterpattern) + 1, factory, sizeof(*factory), NULL); +} + +/* Buckets */ + +PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC) +{ + int is_persistent = php_stream_is_persistent(stream); + php_stream_bucket *bucket; + + bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent); + + if (bucket == NULL) { + return NULL; + } + + bucket->next = bucket->prev = NULL; + + if (is_persistent && !buf_persistent) { + /* all data in a persistent bucket must also be persistent */ + bucket->buf = pemalloc(buflen, 1); + + if (bucket->buf == NULL) { + pefree(bucket, 1); + return NULL; + } + + memcpy(bucket->buf, buf, buflen); + bucket->buflen = buflen; + bucket->own_buf = 1; + } else { + bucket->buf = buf; + bucket->buflen = buflen; + bucket->own_buf = own_buf; + } + bucket->is_persistent = is_persistent; + bucket->refcount = 1; + bucket->brigade = NULL; + + return bucket; +} + +/* Given a bucket, returns a version of that bucket with a writeable buffer. + * If the original bucket has a refcount of 1 and owns its buffer, then it + * is returned unchanged. + * Otherwise, a copy of the buffer is made. + * In both cases, the original bucket is unlinked from its brigade. + * If a copy is made, the original bucket is delref'd. + * */ +PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC) +{ + php_stream_bucket *retval; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + + if (bucket->refcount == 1 && bucket->own_buf) { + return bucket; + } + + retval = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), bucket->is_persistent); + memcpy(retval, bucket, sizeof(*retval)); + + retval->buf = pemalloc(retval->buflen, retval->is_persistent); + memcpy(retval->buf, bucket->buf, retval->buflen); + + retval->refcount = 1; + retval->own_buf = 1; + + php_stream_bucket_delref(bucket TSRMLS_CC); + + return retval; +} + +PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC) +{ + *left = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + *right = (php_stream_bucket*)pecalloc(1, sizeof(php_stream_bucket), in->is_persistent); + + if (*left == NULL || *right == NULL) { + goto exit_fail; + } + + (*left)->buf = pemalloc(length, in->is_persistent); + (*left)->buflen = length; + memcpy((*left)->buf, in->buf, length); + (*left)->refcount = 1; + (*left)->own_buf = 1; + (*left)->is_persistent = in->is_persistent; + + (*right)->buflen = in->buflen - length; + (*right)->buf = pemalloc((*right)->buflen, in->is_persistent); + memcpy((*right)->buf, in->buf + length, (*right)->buflen); + (*right)->refcount = 1; + (*right)->own_buf = 1; + (*right)->is_persistent = in->is_persistent; + + return SUCCESS; + +exit_fail: + if (*right) { + if ((*right)->buf) { + pefree((*right)->buf, in->is_persistent); + } + pefree(*right, in->is_persistent); + } + if (*left) { + if ((*left)->buf) { + pefree((*left)->buf, in->is_persistent); + } + pefree(*left, in->is_persistent); + } + return FAILURE; +} + +PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC) +{ + if (--bucket->refcount == 0) { + if (bucket->own_buf) { + pefree(bucket->buf, bucket->is_persistent); + } + pefree(bucket, bucket->is_persistent); + } +} + +PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + bucket->next = brigade->head; + bucket->prev = NULL; + + if (brigade->head) { + brigade->head->prev = bucket; + } else { + brigade->tail = bucket; + } + brigade->head = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC) +{ + if (brigade->tail == bucket) { + return; + } + + bucket->prev = brigade->tail; + bucket->next = NULL; + + if (brigade->tail) { + brigade->tail->next = bucket; + } else { + brigade->head = bucket; + } + brigade->tail = bucket; + bucket->brigade = brigade; +} + +PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC) +{ + if (bucket->prev) { + bucket->prev->next = bucket->next; + } else if (bucket->brigade) { + bucket->brigade->head = bucket->next; + } + if (bucket->next) { + bucket->next->prev = bucket->prev; + } else if (bucket->brigade) { + bucket->brigade->tail = bucket->prev; + } + bucket->brigade = NULL; + bucket->next = bucket->prev = NULL; +} + + + + + + + + +/* We allow very simple pattern matching for filter factories: + * if "convert.charset.utf-8/sjis" is requested, we search first for an exact + * match. If that fails, we try "convert.charset.*", then "convert.*" + * This means that we don't need to clog up the hashtable with a zillion + * charsets (for example) but still be able to provide them all as filters */ +PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) +{ + HashTable *filter_hash = (FG(stream_filters) ? FG(stream_filters) : &stream_filters_hash); + php_stream_filter_factory *factory = NULL; + php_stream_filter *filter = NULL; + int n; + char *period; + + n = strlen(filtername); + + if (SUCCESS == zend_hash_find(filter_hash, (char*)filtername, n + 1, (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC); + } else if ((period = strrchr(filtername, '.'))) { + /* try a wildcard */ + char *wildname; + + wildname = emalloc(n+3); + memcpy(wildname, filtername, n+1); + period = wildname + (period - filtername); + while (period && !filter) { + *period = '\0'; + strncat(wildname, ".*", 2); + if (SUCCESS == zend_hash_find(filter_hash, wildname, strlen(wildname) + 1, (void**)&factory)) { + filter = factory->create_filter(filtername, filterparams, persistent TSRMLS_CC); + } + + *period = '\0'; + period = strrchr(wildname, '.'); + } + efree(wildname); + } + + if (filter == NULL) { + /* TODO: these need correct docrefs */ + if (factory == NULL) + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to locate filter \"%s\"", filtername); + else + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create or locate filter \"%s\"", filtername); + } + + return filter; +} + +PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC) +{ + php_stream_filter *filter; + + filter = (php_stream_filter*) pemalloc_rel_orig(sizeof(php_stream_filter), persistent); + memset(filter, 0, sizeof(php_stream_filter)); + + filter->fops = fops; + filter->abstract = abstract; + filter->is_persistent = persistent; + + return filter; +} + +PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC) +{ + if (filter->fops->dtor) + filter->fops->dtor(filter TSRMLS_CC); + pefree(filter, filter->is_persistent); +} + +PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC) +{ + filter->next = chain->head; + filter->prev = NULL; + + if (chain->head) { + chain->head->prev = filter; + } else { + chain->tail = filter; + } + chain->head = filter; + filter->chain = chain; + + return SUCCESS; +} + +PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC) +{ + php_stream_filter_prepend_ex(chain, filter TSRMLS_CC); +} + +PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC) +{ + php_stream *stream = chain->stream; + + filter->prev = chain->tail; + filter->next = NULL; + if (chain->tail) { + chain->tail->next = filter; + } else { + chain->head = filter; + } + chain->tail = filter; + filter->chain = chain; + + if (&(stream->readfilters) == chain && (stream->writepos - stream->readpos) > 0) { + /* Let's going ahead and wind anything in the buffer through this filter */ + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out; + php_stream_filter_status_t status; + php_stream_bucket *bucket; + size_t consumed = 0; + + bucket = php_stream_bucket_new(stream, (char*) stream->readbuf + stream->readpos, stream->writepos - stream->readpos, 0, 0 TSRMLS_CC); + php_stream_bucket_append(brig_inp, bucket TSRMLS_CC); + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, &consumed, PSFS_FLAG_NORMAL TSRMLS_CC); + + if (stream->readpos + consumed > (uint)stream->writepos) { + /* No behaving filter should cause this. */ + status = PSFS_ERR_FATAL; + } + + switch (status) { + case PSFS_ERR_FATAL: + while (brig_in.head) { + bucket = brig_in.head; + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + while (brig_out.head) { + bucket = brig_out.head; + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filter failed to process pre-buffered data"); + return FAILURE; + case PSFS_FEED_ME: + /* We don't actually need data yet, + leave this filter in a feed me state until data is needed. + Reset stream's internal read buffer since the filter is "holding" it. */ + stream->readpos = 0; + stream->writepos = 0; + break; + case PSFS_PASS_ON: + /* If any data is consumed, we cannot rely upon the existing read buffer, + as the filtered data must replace the existing data, so invalidate the cache */ + /* note that changes here should be reflected in + main/streams/streams.c::php_stream_fill_read_buffer */ + stream->writepos = 0; + stream->readpos = 0; + + while (brig_outp->head) { + bucket = brig_outp->head; + /* Grow buffer to hold this bucket if need be. + TODO: See warning in main/stream/streams.c::php_stream_fill_read_buffer */ + if (stream->readbuflen - stream->writepos < bucket->buflen) { + stream->readbuflen += bucket->buflen; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, stream->is_persistent); + } + memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); + stream->writepos += bucket->buflen; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + break; + } + } + + return SUCCESS; +} + +PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC) +{ + if (php_stream_filter_append_ex(chain, filter TSRMLS_CC) != SUCCESS) { + if (chain->head == filter) { + chain->head = NULL; + chain->tail = NULL; + } else { + filter->prev->next = NULL; + chain->tail = filter->prev; + } + } +} + +PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC) +{ + php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp; + php_stream_bucket *bucket; + php_stream_filter_chain *chain; + php_stream_filter *current; + php_stream *stream; + size_t flushed_size = 0; + long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC); + + if (!filter->chain || !filter->chain->stream) { + /* Filter is not attached to a chain, or chain is somehow not part of a stream */ + return FAILURE; + } + + chain = filter->chain; + stream = chain->stream; + + for(current = filter; current; current = current->next) { + php_stream_filter_status_t status; + + status = filter->fops->filter(stream, filter, inp, outp, NULL, flags TSRMLS_CC); + if (status == PSFS_FEED_ME) { + /* We've flushed the data far enough */ + return SUCCESS; + } + if (status == PSFS_ERR_FATAL) { + return FAILURE; + } + /* Otherwise we have data available to PASS_ON + Swap the brigades and continue */ + brig_temp = inp; + inp = outp; + outp = brig_temp; + outp->head = NULL; + outp->tail = NULL; + + flags = PSFS_FLAG_NORMAL; + } + + /* Last filter returned data via PSFS_PASS_ON + Do something with it */ + + for(bucket = inp->head; bucket; bucket = bucket->next) { + flushed_size += bucket->buflen; + } + + if (flushed_size == 0) { + /* Unlikely, but possible */ + return SUCCESS; + } + + if (chain == &(stream->readfilters)) { + /* Dump any newly flushed data to the read buffer */ + if (stream->readpos > 0) { + /* Back the buffer up */ + memcpy(stream->readbuf, stream->readbuf + stream->readpos, stream->writepos - stream->readpos); + stream->readpos = 0; + stream->writepos -= stream->readpos; + } + if (flushed_size > (stream->readbuflen - stream->writepos)) { + /* Grow the buffer */ + stream->readbuf = perealloc(stream->readbuf, stream->writepos + flushed_size + stream->chunk_size, stream->is_persistent); + } + while ((bucket = inp->head)) { + memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); + stream->writepos += bucket->buflen; + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + } else if (chain == &(stream->writefilters)) { + /* Send flushed data to the stream */ + while ((bucket = inp->head)) { + stream->ops->write(stream, bucket->buf, bucket->buflen TSRMLS_CC); + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + } + + return SUCCESS; +} + +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC) +{ + if (filter->prev) { + filter->prev->next = filter->next; + } else { + filter->chain->head = filter->next; + } + if (filter->next) { + filter->next->prev = filter->prev; + } else { + filter->chain->tail = filter->prev; + } + + if (filter->rsrc_id > 0) { + zend_list_delete(filter->rsrc_id); + } + + if (call_dtor) { + php_stream_filter_free(filter TSRMLS_CC); + return NULL; + } + return filter; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c new file mode 100644 index 0000000..9c051a5 --- /dev/null +++ b/main/streams/glob_wrapper.c @@ -0,0 +1,291 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_streams_int.h" + +#ifdef HAVE_GLOB +# ifndef PHP_WIN32 +# include +# else +# include "win32/glob.h" +# endif +#endif + +#ifdef HAVE_GLOB +#ifndef GLOB_ONLYDIR +#define GLOB_ONLYDIR (1<<30) +#define GLOB_FLAGMASK (~GLOB_ONLYDIR) +#else +#define GLOB_FLAGMASK (~0) +#endif + +typedef struct { + glob_t glob; + size_t index; + int flags; + char *path; + size_t path_len; + char *pattern; + size_t pattern_len; +} glob_s_t; + +PHPAPI char* _php_glob_stream_get_path(php_stream *stream, int copy, int *plen STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + + if (pglob && pglob->path) { + if (plen) { + *plen = pglob->path_len; + } + if (copy) { + return estrndup(pglob->path, pglob->path_len); + } else { + return pglob->path; + } + } else { + if (plen) { + *plen = 0; + } + return NULL; + } +} +/* }}} */ + +PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, int copy, int *plen STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + + if (pglob && pglob->pattern) { + if (plen) { + *plen = pglob->pattern_len; + } + if (copy) { + return estrndup(pglob->pattern, pglob->pattern_len); + } else { + return pglob->pattern; + } + } else { + if (plen) { + *plen = 0; + } + return NULL; + } +} +/* }}} */ + +PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + + if (pglob) { + if (pflags) { + *pflags = pglob->flags; + } + return pglob->glob.gl_pathc; + } else { + if (pflags) { + *pflags = 0; + } + return 0; + } +} +/* }}} */ + +static void php_glob_stream_path_split(glob_s_t *pglob, char *path, int get_path, char **p_file TSRMLS_DC) /* {{{ */ +{ + char *pos, *gpath = path; + + if ((pos = strrchr(path, '/')) != NULL) { + path = pos+1; + } +#if defined(PHP_WIN32) || defined(NETWARE) + if ((pos = strrchr(path, '\\')) != NULL) { + path = pos+1; + } +#endif + + *p_file = path; + + if (get_path) { + if (pglob->path) { + efree(pglob->path); + } + if (path != gpath) { + path--; + } + pglob->path_len = path - gpath; + pglob->path = estrndup(gpath, pglob->path_len); + } +} +/* }}} */ + +static size_t php_glob_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + php_stream_dirent *ent = (php_stream_dirent*)buf; + char *path; + + /* avoid problems if someone mis-uses the stream */ + if (count == sizeof(php_stream_dirent) && pglob) { + 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); + } + pglob->index = pglob->glob.gl_pathc; + if (pglob->path) { + efree(pglob->path); + pglob->path = NULL; + } + } + + return 0; +} +/* }}} */ + +static int php_glob_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + + if (pglob) { + pglob->index = 0; + globfree(&pglob->glob); + if (pglob->path) { + efree(pglob->path); + } + if (pglob->pattern) { + efree(pglob->pattern); + } + } + efree(stream->abstract); + return 0; +} +/* {{{ */ + +static int php_glob_stream_rewind(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) /* {{{ */ +{ + glob_s_t *pglob = (glob_s_t *)stream->abstract; + + if (pglob) { + pglob->index = 0; + if (pglob->path) { + efree(pglob->path); + pglob->path = NULL; + } + } + return 0; +} +/* }}} */ + +php_stream_ops php_glob_stream_ops = { + NULL, php_glob_stream_read, + php_glob_stream_close, NULL, + "glob", + php_glob_stream_rewind, + NULL, /* cast */ + NULL, /* stat */ + NULL /* set_option */ +}; + + /* {{{ php_glob_stream_opener */ +static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + glob_s_t *pglob; + int ret; + char *tmp, *pos; + + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path TSRMLS_CC)) { + return NULL; + } + + if (!strncmp(path, "glob://", sizeof("glob://")-1)) { + path += sizeof("glob://")-1; + if (opened_path) { + *opened_path = estrdup(path); + } + } + + pglob = ecalloc(sizeof(*pglob), 1); + + if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) { +#ifdef GLOB_NOMATCH + if (GLOB_NOMATCH != ret) +#endif + { + efree(pglob); + return NULL; + } + } + + pos = path; + if ((tmp = strrchr(pos, '/')) != NULL) { + pos = tmp+1; + } +#if defined(PHP_WIN32) || defined(NETWARE) + if ((tmp = strrchr(pos, '\\')) != NULL) { + pos = tmp+1; + } +#endif + + pglob->pattern_len = strlen(pos); + pglob->pattern = estrndup(pos, pglob->pattern_len); + + pglob->flags |= GLOB_APPEND; + + if (pglob->glob.gl_pathc) { + php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp TSRMLS_CC); + } else { + php_glob_stream_path_split(pglob, path, 1, &tmp TSRMLS_CC); + } + + return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode); +} +/* }}} */ + +static php_stream_wrapper_ops php_glob_stream_wrapper_ops = { + NULL, + NULL, + NULL, + NULL, + php_glob_stream_opener, + "glob", + NULL, + NULL, + NULL, + NULL +}; + +php_stream_wrapper php_glob_stream_wrapper = { + &php_glob_stream_wrapper_ops, + NULL, + 0 +}; +#endif /* HAVE_GLOB */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/memory.c b/main/streams/memory.c new file mode 100644 index 0000000..328d3be --- /dev/null +++ b/main/streams/memory.c @@ -0,0 +1,758 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define _GNU_SOURCE +#include "php.h" + +PHPAPI int php_url_decode(char *str, int len); +PHPAPI unsigned char *php_base64_decode(const unsigned char *str, int length, int *ret_length); + +/* Memory streams use a dynamic memory buffer to emulate a stream. + * You can use php_stream_memory_open to create a readonly stream + * from an existing memory buffer. + */ + +/* Temp streams are streams that uses memory streams as long their + * size is less than a given memory amount. When a write operation + * exceeds that limit the content is written to a temporary file. + */ + +/* {{{ ------- MEMORY stream implementation -------*/ + +typedef struct { + char *data; + size_t fpos; + size_t fsize; + size_t smax; + int mode; +} php_stream_memory_data; + + +/* {{{ */ +static size_t php_stream_memory_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + assert(ms != NULL); + + if (ms->mode & TEMP_STREAM_READONLY) { + return 0; + } + if (ms->fpos + count > ms->fsize) { + char *tmp; + + if (!ms->data) { + tmp = emalloc(ms->fpos + count); + } else { + tmp = erealloc(ms->data, ms->fpos + count); + } + if (!tmp) { + count = ms->fsize - ms->fpos + 1; + } else { + ms->data = tmp; + ms->fsize = ms->fpos + count; + } + } + if (!ms->data) + count = 0; + if (count) { + assert(buf!= NULL); + memcpy(ms->data+ms->fpos, (char*)buf, count); + ms->fpos += count; + } + return count; +} +/* }}} */ + + +/* {{{ */ +static size_t php_stream_memory_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + assert(ms != NULL); + + if (ms->fpos + count >= ms->fsize) { + count = ms->fsize - ms->fpos; + stream->eof = 1; + } + if (count) { + assert(ms->data!= NULL); + assert(buf!= NULL); + memcpy(buf, ms->data+ms->fpos, count); + ms->fpos += count; + } + return count; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_memory_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + assert(ms != NULL); + + if (ms->data && close_handle && ms->mode != TEMP_STREAM_READONLY) { + efree(ms->data); + } + efree(ms); + return 0; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_memory_flush(php_stream *stream TSRMLS_DC) +{ + /* nothing to do here */ + return 0; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_memory_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + assert(ms != NULL); + + switch(whence) { + case SEEK_CUR: + if (offset < 0) { + if (ms->fpos < (size_t)(-offset)) { + ms->fpos = 0; + *newoffs = -1; + return -1; + } else { + ms->fpos = ms->fpos + offset; + *newoffs = ms->fpos; + stream->eof = 0; + return 0; + } + } else { + if (ms->fpos + (size_t)(offset) > ms->fsize) { + ms->fpos = ms->fsize; + *newoffs = -1; + return -1; + } else { + ms->fpos = ms->fpos + offset; + *newoffs = ms->fpos; + stream->eof = 0; + return 0; + } + } + case SEEK_SET: + if (ms->fsize < (size_t)(offset)) { + ms->fpos = ms->fsize; + *newoffs = -1; + return -1; + } else { + ms->fpos = offset; + *newoffs = ms->fpos; + stream->eof = 0; + return 0; + } + case SEEK_END: + if (offset > 0) { + ms->fpos = ms->fsize; + *newoffs = -1; + return -1; + } else if (ms->fsize < (size_t)(-offset)) { + ms->fpos = 0; + *newoffs = -1; + return -1; + } else { + ms->fpos = ms->fsize + offset; + *newoffs = ms->fpos; + stream->eof = 0; + return 0; + } + default: + *newoffs = ms->fpos; + return -1; + } +} +/* }}} */ + +/* {{{ */ +static int php_stream_memory_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + return FAILURE; +} +/* }}} */ + +static int php_stream_memory_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ +{ + time_t timestamp = 0; + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + assert(ms != NULL); + + memset(ssb, 0, sizeof(php_stream_statbuf)); + /* read-only across the board */ + + ssb->sb.st_mode = ms->mode & TEMP_STREAM_READONLY ? 0444 : 0666; + + ssb->sb.st_size = ms->fsize; + ssb->sb.st_mode |= S_IFREG; /* regular file */ + +#ifdef NETWARE + ssb->sb.st_mtime.tv_sec = timestamp; + ssb->sb.st_atime.tv_sec = timestamp; + ssb->sb.st_ctime.tv_sec = timestamp; +#else + ssb->sb.st_mtime = timestamp; + ssb->sb.st_atime = timestamp; + ssb->sb.st_ctime = timestamp; +#endif + + ssb->sb.st_nlink = 1; + ssb->sb.st_rdev = -1; + /* this is only for APC, so use /dev/null device - no chance of conflict there! */ + ssb->sb.st_dev = 0xC; + /* generate unique inode number for alias/filename, so no phars will conflict */ + ssb->sb.st_ino = 0; + +#ifndef PHP_WIN32 + ssb->sb.st_blksize = -1; +#endif + +#if !defined(PHP_WIN32) && !defined(__BEOS__) + ssb->sb.st_blocks = -1; +#endif + + return 0; +} +/* }}} */ + +static int php_stream_memory_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */ +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + size_t newsize; + + switch(option) { + case PHP_STREAM_OPTION_TRUNCATE_API: + switch (value) { + case PHP_STREAM_TRUNCATE_SUPPORTED: + return PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_TRUNCATE_SET_SIZE: + if (ms->mode & TEMP_STREAM_READONLY) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + newsize = *(size_t*)ptrparam; + if (newsize <= ms->fsize) { + if (newsize < ms->fpos) { + ms->fpos = newsize; + } + } else { + ms->data = erealloc(ms->data, newsize); + memset(ms->data+ms->fsize, 0, newsize - ms->fsize); + ms->fsize = newsize; + } + ms->fsize = newsize; + return PHP_STREAM_OPTION_RETURN_OK; + } + default: + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + } +} +/* }}} */ + +PHPAPI php_stream_ops php_stream_memory_ops = { + php_stream_memory_write, php_stream_memory_read, + php_stream_memory_close, php_stream_memory_flush, + "MEMORY", + php_stream_memory_seek, + php_stream_memory_cast, + php_stream_memory_stat, + php_stream_memory_set_option +}; + + +/* {{{ */ +PHPAPI php_stream *_php_stream_memory_create(int mode STREAMS_DC TSRMLS_DC) +{ + php_stream_memory_data *self; + php_stream *stream; + + self = emalloc(sizeof(*self)); + self->data = NULL; + self->fpos = 0; + self->fsize = 0; + self->smax = ~0u; + self->mode = mode; + + 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; + return stream; +} +/* }}} */ + + +/* {{{ */ +PHPAPI php_stream *_php_stream_memory_open(int mode, char *buf, size_t length STREAMS_DC TSRMLS_DC) +{ + php_stream *stream; + php_stream_memory_data *ms; + + if ((stream = php_stream_memory_create_rel(mode)) != NULL) { + ms = (php_stream_memory_data*)stream->abstract; + + if (mode == TEMP_STREAM_READONLY || mode == TEMP_STREAM_TAKE_BUFFER) { + /* use the buffer directly */ + ms->data = buf; + ms->fsize = length; + } else { + if (length) { + assert(buf != NULL); + php_stream_write(stream, buf, length); + } + } + } + return stream; +} +/* }}} */ + + +/* {{{ */ +PHPAPI char *_php_stream_memory_get_buffer(php_stream *stream, size_t *length STREAMS_DC TSRMLS_DC) +{ + php_stream_memory_data *ms = (php_stream_memory_data*)stream->abstract; + + assert(ms != NULL); + assert(length != 0); + + *length = ms->fsize; + return ms->data; +} +/* }}} */ + +/* }}} */ + +/* {{{ ------- TEMP stream implementation -------*/ + +typedef struct { + php_stream *innerstream; + size_t smax; + int mode; + zval* meta; +} php_stream_temp_data; + + +/* {{{ */ +static size_t php_stream_temp_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + assert(ts != NULL); + + if (!ts->innerstream) { + return -1; + } + if (php_stream_is(ts->innerstream, PHP_STREAM_IS_MEMORY)) { + size_t memsize; + char *membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize); + + if (memsize + count >= ts->smax) { + php_stream *file = php_stream_fopen_tmpfile(); + php_stream_write(file, membuf, memsize); + 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); +} +/* }}} */ + + +/* {{{ */ +static size_t php_stream_temp_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + size_t got; + + assert(ts != NULL); + + if (!ts->innerstream) { + return -1; + } + + got = php_stream_read(ts->innerstream, buf, count); + + stream->eof = ts->innerstream->eof; + + return got; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_temp_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + int ret; + + assert(ts != NULL); + + if (ts->innerstream) { + ret = php_stream_free_enclosed(ts->innerstream, PHP_STREAM_FREE_CLOSE | (close_handle ? 0 : PHP_STREAM_FREE_PRESERVE_HANDLE)); + } else { + ret = 0; + } + + if (ts->meta) { + zval_ptr_dtor(&ts->meta); + } + + efree(ts); + + return ret; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_temp_flush(php_stream *stream TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + assert(ts != NULL); + + return ts->innerstream ? php_stream_flush(ts->innerstream) : -1; +} +/* }}} */ + + +/* {{{ */ +static int php_stream_temp_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + int ret; + + assert(ts != NULL); + + if (!ts->innerstream) { + *newoffs = -1; + return -1; + } + ret = php_stream_seek(ts->innerstream, offset, whence); + *newoffs = php_stream_tell(ts->innerstream); + stream->eof = ts->innerstream->eof; + + return ret; +} +/* }}} */ + +/* {{{ */ +static int php_stream_temp_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + php_stream *file; + size_t memsize; + char *membuf; + off_t pos; + + assert(ts != NULL); + + if (!ts->innerstream) { + return FAILURE; + } + if (php_stream_is(ts->innerstream, PHP_STREAM_IS_STDIO)) { + return php_stream_cast(ts->innerstream, castas, ret, 0); + } + + /* we are still using a memory based backing. If they are if we can be + * a FILE*, say yes because we can perform the conversion. + * If they actually want to perform the conversion, we need to switch + * the memory stream to a tmpfile stream */ + + if (ret == NULL && castas == PHP_STREAM_AS_STDIO) { + return SUCCESS; + } + + /* say "no" to other stream forms */ + if (ret == NULL) { + return FAILURE; + } + + /* perform the conversion and then pass the request on to the innerstream */ + membuf = php_stream_memory_get_buffer(ts->innerstream, &memsize); + file = php_stream_fopen_tmpfile(); + php_stream_write(file, membuf, memsize); + pos = php_stream_tell(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); +} +/* }}} */ + +static int php_stream_temp_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */ +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + + if (!ts || !ts->innerstream) { + return -1; + } + return php_stream_stat(ts->innerstream, ssb); +} +/* }}} */ + +static int php_stream_temp_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) /* {{{ */ +{ + php_stream_temp_data *ts = (php_stream_temp_data*)stream->abstract; + + switch(option) { + case PHP_STREAM_OPTION_META_DATA_API: + if (ts->meta) { + zend_hash_copy(Z_ARRVAL_P((zval*)ptrparam), Z_ARRVAL_P(ts->meta), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*)); + } + return PHP_STREAM_OPTION_RETURN_OK; + default: + if (ts->innerstream) { + return php_stream_set_option(ts->innerstream, option, value, ptrparam); + } + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + } +} +/* }}} */ + +PHPAPI php_stream_ops php_stream_temp_ops = { + php_stream_temp_write, php_stream_temp_read, + php_stream_temp_close, php_stream_temp_flush, + "TEMP", + php_stream_temp_seek, + php_stream_temp_cast, + php_stream_temp_stat, + php_stream_temp_set_option +}; + +/* }}} */ + +/* {{{ _php_stream_temp_create */ +PHPAPI php_stream *_php_stream_temp_create(int mode, size_t max_memory_usage STREAMS_DC TSRMLS_DC) +{ + php_stream_temp_data *self; + php_stream *stream; + + self = ecalloc(1, sizeof(*self)); + self->smax = max_memory_usage; + self->mode = mode; + self->meta = NULL; + 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_encloses(stream, self->innerstream); + + return stream; +} +/* }}} */ + + +/* {{{ _php_stream_temp_open */ +PHPAPI php_stream *_php_stream_temp_open(int mode, size_t max_memory_usage, char *buf, size_t length STREAMS_DC TSRMLS_DC) +{ + php_stream *stream; + php_stream_temp_data *ts; + off_t newoffs; + + if ((stream = php_stream_temp_create_rel(mode, max_memory_usage)) != NULL) { + if (length) { + assert(buf != NULL); + php_stream_temp_write(stream, buf, length TSRMLS_CC); + php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC); + } + ts = (php_stream_temp_data*)stream->abstract; + assert(ts != NULL); + ts->mode = mode; + } + return stream; +} +/* }}} */ + +PHPAPI php_stream_ops php_stream_rfc2397_ops = { + php_stream_temp_write, php_stream_temp_read, + php_stream_temp_close, php_stream_temp_flush, + "RFC2397", + php_stream_temp_seek, + php_stream_temp_cast, + php_stream_temp_stat, + php_stream_temp_set_option +}; + +static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + php_stream *stream; + php_stream_temp_data *ts; + char *comma, *semi, *sep, *key; + size_t mlen, dlen, plen, vlen; + off_t newoffs; + zval *meta = NULL; + int base64 = 0, ilen; + + if (memcmp(path, "data:", 5)) { + return NULL; + } + + path += 5; + dlen = strlen(path); + + if (dlen >= 2 && path[0] == '/' && path[1] == '/') { + dlen -= 2; + path += 2; + } + + if ((comma = memchr(path, ',', dlen)) == NULL) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: no comma in URL"); + return NULL; + } + + if (comma != path) { + /* meta info */ + mlen = comma - path; + dlen -= mlen; + semi = memchr(path, ';', mlen); + sep = memchr(path, '/', mlen); + + if (!semi && !sep) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type"); + return NULL; + } + + MAKE_STD_ZVAL(meta); + array_init(meta); + if (!semi) { /* there is only a mime type */ + add_assoc_stringl(meta, "mediatype", path, mlen, 1); + mlen = 0; + } else if (sep && sep < semi) { /* there is a mime type */ + plen = semi - path; + add_assoc_stringl(meta, "mediatype", path, plen, 1); + mlen -= plen; + path += plen; + } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */ + zval_ptr_dtor(&meta); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal media type"); + return NULL; + } + /* get parameters and potentially ';base64' */ + while(semi && (semi == path)) { + path++; + mlen--; + sep = memchr(path, '=', mlen); + semi = memchr(path, ';', mlen); + if (!sep || (semi && semi < sep)) { /* must be ';base64' or failure */ + if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) { + /* must be error since parameters are only allowed after mediatype and we have no '=' sign */ + zval_ptr_dtor(&meta); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal parameter"); + return NULL; + } + base64 = 1; + mlen -= sizeof("base64") - 1; + path += sizeof("base64") - 1; + break; + } + /* found parameter ... the heart of cs ppl lies in +1/-1 or was it +2 this time? */ + plen = sep - path; + vlen = (semi ? semi - sep : mlen - plen) - 1 /* '=' */; + key = estrndup(path, plen); + add_assoc_stringl_ex(meta, key, plen + 1, sep + 1, vlen, 1); + efree(key); + plen += vlen + 1; + mlen -= plen; + path += plen; + } + if (mlen) { + zval_ptr_dtor(&meta); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: illegal URL"); + return NULL; + } + } else { + MAKE_STD_ZVAL(meta); + array_init(meta); + } + add_assoc_bool(meta, "base64", base64); + + /* skip ',' */ + comma++; + dlen--; + + if (base64) { + comma = (char*)php_base64_decode((const unsigned char *)comma, dlen, &ilen); + if (!comma) { + zval_ptr_dtor(&meta); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "rfc2397: unable to decode"); + return NULL; + } + } else { + comma = estrndup(comma, dlen); + ilen = dlen = php_url_decode(comma, dlen); + } + + if ((stream = php_stream_temp_create_rel(0, ~0u)) != NULL) { + /* store data */ + php_stream_temp_write(stream, comma, ilen TSRMLS_CC); + php_stream_temp_seek(stream, 0, SEEK_SET, &newoffs TSRMLS_CC); + /* set special stream stuff (enforce exact mode) */ + vlen = strlen(mode); + if (vlen >= sizeof(stream->mode)) { + vlen = sizeof(stream->mode) - 1; + } + memcpy(stream->mode, mode, vlen); + stream->mode[vlen] = '\0'; + stream->ops = &php_stream_rfc2397_ops; + ts = (php_stream_temp_data*)stream->abstract; + assert(ts != NULL); + ts->mode = mode && mode[0] == 'r' && mode[1] != '+' ? TEMP_STREAM_READONLY : 0; + ts->meta = meta; + } + efree(comma); + + return stream; +} + +PHPAPI php_stream_wrapper_ops php_stream_rfc2397_wops = { + php_stream_url_wrap_rfc2397, + NULL, /* close */ + NULL, /* fstat */ + NULL, /* stat */ + NULL, /* opendir */ + "RFC2397", + NULL, /* unlink */ + NULL, /* rename */ + NULL, /* mkdir */ + NULL /* rmdir */ +}; + +PHPAPI php_stream_wrapper php_stream_rfc2397_wrapper = { + &php_stream_rfc2397_wops, + NULL, + 1, /* is_url */ +}; + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/mmap.c b/main/streams/mmap.c new file mode 100644 index 0000000..4f9388c --- /dev/null +++ b/main/streams/mmap.c @@ -0,0 +1,75 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* Memory Mapping interface for streams */ +#include "php.h" +#include "php_streams_int.h" + +PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC) +{ + php_stream_mmap_range range; + + range.offset = offset; + range.length = length; + range.mode = mode; + range.mapped = NULL; + + /* For now, we impose an arbitrary limit to avoid + * runaway swapping when large files are passed thru. */ + if (length > 4 * 1024 * 1024) { + return NULL; + } + + if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_MAP_RANGE, &range)) { + if (mapped_len) { + *mapped_len = range.length; + } + return range.mapped; + } + return NULL; +} + +PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC) +{ + return php_stream_set_option(stream, PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_UNMAP, NULL) == PHP_STREAM_OPTION_RETURN_OK ? 1 : 0; +} + +PHPAPI int _php_stream_mmap_unmap_ex(php_stream *stream, off_t readden TSRMLS_DC) +{ + int ret = 1; + + if (php_stream_seek(stream, readden, SEEK_CUR) != 0) { + ret = 0; + } + if (php_stream_mmap_unmap(stream) == 0) { + ret = 0; + } + + return ret; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h new file mode 100644 index 0000000..240f6e0 --- /dev/null +++ b/main/streams/php_stream_context.h @@ -0,0 +1,136 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* Stream context and status notification related definitions */ + +/* callback for status notifications */ +typedef void (*php_stream_notification_func)(php_stream_context *context, + int notifycode, int severity, + char *xmsg, int xcode, + size_t bytes_sofar, size_t bytes_max, + void * ptr TSRMLS_DC); + +#define PHP_STREAM_NOTIFIER_PROGRESS 1 + +/* Attempt to fetch context from the zval passed, + 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(TSRMLS_C)) : \ + (nocontext) ? NULL : \ + FG(default_context) ? FG(default_context) : \ + (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); } + +typedef struct _php_stream_notifier php_stream_notifier; + +struct _php_stream_notifier { + php_stream_notification_func func; + void (*dtor)(php_stream_notifier *notifier); + void *ptr; + int mask; + size_t progress, progress_max; /* position for progress notification */ +}; + +struct _php_stream_context { + php_stream_notifier *notifier; + zval *options; /* hash keyed by wrapper family or specific wrapper */ + zval *links; /* hash keyed by hostent for connection pooling */ + int rsrc_id; /* used for auto-cleanup */ +}; + +BEGIN_EXTERN_C() +PHPAPI void php_stream_context_free(php_stream_context *context); +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, + const char *wrappername, const char *optionname, zval *optionvalue); + +PHPAPI int php_stream_context_get_link(php_stream_context *context, + const char *hostent, php_stream **stream); +PHPAPI int php_stream_context_set_link(php_stream_context *context, + const char *hostent, php_stream *stream); +PHPAPI int php_stream_context_del_link(php_stream_context *context, + php_stream *stream); + +PHPAPI php_stream_notifier *php_stream_notification_alloc(void); +PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); +END_EXTERN_C() + +/* not all notification codes are implemented */ +#define PHP_STREAM_NOTIFY_RESOLVE 1 +#define PHP_STREAM_NOTIFY_CONNECT 2 +#define PHP_STREAM_NOTIFY_AUTH_REQUIRED 3 +#define PHP_STREAM_NOTIFY_MIME_TYPE_IS 4 +#define PHP_STREAM_NOTIFY_FILE_SIZE_IS 5 +#define PHP_STREAM_NOTIFY_REDIRECTED 6 +#define PHP_STREAM_NOTIFY_PROGRESS 7 +#define PHP_STREAM_NOTIFY_COMPLETED 8 +#define PHP_STREAM_NOTIFY_FAILURE 9 +#define PHP_STREAM_NOTIFY_AUTH_RESULT 10 + +#define PHP_STREAM_NOTIFY_SEVERITY_INFO 0 +#define PHP_STREAM_NOTIFY_SEVERITY_WARN 1 +#define PHP_STREAM_NOTIFY_SEVERITY_ERR 2 + +BEGIN_EXTERN_C() +PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity, + char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC); +PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context); +END_EXTERN_C() + +#define php_stream_notify_info(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) { \ + php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_INFO, \ + (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while (0) + +#define php_stream_notify_progress(context, bsofar, bmax) do { if ((context) && (context)->notifier) { \ + php_stream_notification_notify((context), PHP_STREAM_NOTIFY_PROGRESS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ + NULL, 0, (bsofar), (bmax), NULL TSRMLS_CC); } } while(0) + +#define php_stream_notify_progress_init(context, sofar, bmax) do { if ((context) && (context)->notifier) { \ + (context)->notifier->progress = (sofar); \ + (context)->notifier->progress_max = (bmax); \ + (context)->notifier->mask |= PHP_STREAM_NOTIFIER_PROGRESS; \ + php_stream_notify_progress((context), (sofar), (bmax)); } } while (0) + +#define php_stream_notify_progress_increment(context, dsofar, dmax) do { if ((context) && (context)->notifier && (context)->notifier->mask & PHP_STREAM_NOTIFIER_PROGRESS) { \ + (context)->notifier->progress += (dsofar); \ + (context)->notifier->progress_max += (dmax); \ + php_stream_notify_progress((context), (context)->notifier->progress, (context)->notifier->progress_max); } } while (0) + +#define php_stream_notify_file_size(context, file_size, xmsg, xcode) do { if ((context) && (context)->notifier) { \ + php_stream_notification_notify((context), PHP_STREAM_NOTIFY_FILE_SIZE_IS, PHP_STREAM_NOTIFY_SEVERITY_INFO, \ + (xmsg), (xcode), 0, (file_size), NULL TSRMLS_CC); } } while(0) + +#define php_stream_notify_error(context, code, xmsg, xcode) do { if ((context) && (context)->notifier) {\ + php_stream_notification_notify((context), (code), PHP_STREAM_NOTIFY_SEVERITY_ERR, \ + (xmsg), (xcode), 0, 0, NULL TSRMLS_CC); } } while(0) + + +/* + * 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/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h new file mode 100644 index 0000000..abb5906 --- /dev/null +++ b/main/streams/php_stream_filter_api.h @@ -0,0 +1,162 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + | With suggestions from: | + | Moriyoshi Koizumi | + | Sara Golemon | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* The filter API works on the principle of "Bucket-Brigades". This is + * partially inspired by the Apache 2 method of doing things, although + * it is intentially a light-weight implementation. + * + * Each stream can have a chain of filters for reading and another for writing. + * + * When data is written to the stream, it is placed into a bucket and placed at + * the start of the input brigade. + * + * The first filter in the chain is invoked on the brigade and (depending on + * it's return value), the next filter is invoked and so on. + * */ + +#define PHP_STREAM_FILTER_READ 0x0001 +#define PHP_STREAM_FILTER_WRITE 0x0002 +#define PHP_STREAM_FILTER_ALL (PHP_STREAM_FILTER_READ | PHP_STREAM_FILTER_WRITE) + +typedef struct _php_stream_bucket php_stream_bucket; +typedef struct _php_stream_bucket_brigade php_stream_bucket_brigade; + +struct _php_stream_bucket { + php_stream_bucket *next, *prev; + php_stream_bucket_brigade *brigade; + + char *buf; + size_t buflen; + /* if non-zero, buf should be pefreed when the bucket is destroyed */ + int own_buf; + int is_persistent; + + /* destroy this struct when refcount falls to zero */ + int refcount; +}; + +struct _php_stream_bucket_brigade { + php_stream_bucket *head, *tail; +}; + +typedef enum { + PSFS_ERR_FATAL, /* error in data stream */ + PSFS_FEED_ME, /* filter needs more data; stop processing chain until more is available */ + PSFS_PASS_ON /* filter generated output buckets; pass them on to next in chain */ +} php_stream_filter_status_t; + +/* Buckets API. */ +BEGIN_EXTERN_C() +PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC); +PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC); +PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC); +#define php_stream_bucket_addref(bucket) (bucket)->refcount++ +PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); +PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC); +PHPAPI void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC); +PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bucket TSRMLS_DC); +END_EXTERN_C() + +#define PSFS_FLAG_NORMAL 0 /* regular read/write */ +#define PSFS_FLAG_FLUSH_INC 1 /* an incremental flush */ +#define PSFS_FLAG_FLUSH_CLOSE 2 /* final flush prior to closing */ + +typedef struct _php_stream_filter_ops { + + php_stream_filter_status_t (*filter)( + php_stream *stream, + php_stream_filter *thisfilter, + php_stream_bucket_brigade *buckets_in, + php_stream_bucket_brigade *buckets_out, + size_t *bytes_consumed, + int flags + TSRMLS_DC); + + void (*dtor)(php_stream_filter *thisfilter TSRMLS_DC); + + const char *label; + +} php_stream_filter_ops; + +typedef struct _php_stream_filter_chain { + php_stream_filter *head, *tail; + + /* Owning stream */ + php_stream *stream; +} php_stream_filter_chain; + +struct _php_stream_filter { + php_stream_filter_ops *fops; + void *abstract; /* for use by filter implementation */ + php_stream_filter *next; + php_stream_filter *prev; + int is_persistent; + + /* link into stream and chain */ + php_stream_filter_chain *chain; + + /* buffered buckets */ + php_stream_bucket_brigade buffer; + + /* filters are auto_registered when they're applied */ + int rsrc_id; +}; + +/* stack filter onto a stream */ +BEGIN_EXTERN_C() +PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC); +PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC); +PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC); +PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter TSRMLS_DC); +PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish TSRMLS_DC); +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor TSRMLS_DC); +PHPAPI void php_stream_filter_free(php_stream_filter *filter TSRMLS_DC); +PHPAPI php_stream_filter *_php_stream_filter_alloc(php_stream_filter_ops *fops, void *abstract, int persistent STREAMS_DC TSRMLS_DC); +END_EXTERN_C() +#define php_stream_filter_alloc(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_CC TSRMLS_CC) +#define php_stream_filter_alloc_rel(fops, thisptr, persistent) _php_stream_filter_alloc((fops), (thisptr), (persistent) STREAMS_REL_CC TSRMLS_CC) +#define php_stream_filter_prepend(chain, filter) _php_stream_filter_prepend((chain), (filter) TSRMLS_CC) +#define php_stream_filter_append(chain, filter) _php_stream_filter_append((chain), (filter) TSRMLS_CC) +#define php_stream_filter_flush(filter, finish) _php_stream_filter_flush((filter), (finish) TSRMLS_CC) + +#define php_stream_is_filtered(stream) ((stream)->readfilters.head || (stream)->writefilters.head) + +typedef struct _php_stream_filter_factory { + php_stream_filter *(*create_filter)(const char *filtername, zval *filterparams, int persistent TSRMLS_DC); +} php_stream_filter_factory; + +BEGIN_EXTERN_C() +PHPAPI int php_stream_filter_register_factory(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC); +PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern TSRMLS_DC); +PHPAPI int php_stream_filter_register_factory_volatile(const char *filterpattern, php_stream_filter_factory *factory TSRMLS_DC); +PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC); +END_EXTERN_C() + +/* + * 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/streams/php_stream_glob_wrapper.h b/main/streams/php_stream_glob_wrapper.h new file mode 100644 index 0000000..330e917 --- /dev/null +++ b/main/streams/php_stream_glob_wrapper.h @@ -0,0 +1,44 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +PHPAPI extern php_stream_wrapper php_glob_stream_wrapper; +PHPAPI extern php_stream_ops php_glob_stream_ops; + +BEGIN_EXTERN_C() + +PHPAPI char* _php_glob_stream_get_path(php_stream *stream, int copy, int *plen STREAMS_DC TSRMLS_DC); +#define php_glob_stream_get_path(stream, copy, plen) _php_glob_stream_get_path((stream), (copy), (plen) STREAMS_CC TSRMLS_CC) + +PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, int copy, int *plen STREAMS_DC TSRMLS_DC); +#define php_glob_stream_get_pattern(stream, copy, plen) _php_glob_stream_get_pattern((stream), (copy), (plen) STREAMS_CC TSRMLS_CC) + +PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC TSRMLS_DC); +#define php_glob_stream_get_count(stream, pflags) _php_glob_stream_get_count((stream), (pflags) STREAMS_CC TSRMLS_CC) + +END_EXTERN_C() + +/* + * 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/streams/php_stream_mmap.h b/main/streams/php_stream_mmap.h new file mode 100644 index 0000000..7895ac6 --- /dev/null +++ b/main/streams/php_stream_mmap.h @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +/* Memory Mapping interface for streams. + * The intention is to provide a uniform interface over the most common + * operations that are used within PHP itself, rather than a complete + * API for all memory mapping needs. + * + * ATM, we support only mmap(), but win32 memory mapping support will + * follow soon. + * */ + +typedef enum { + /* Does the stream support mmap ? */ + PHP_STREAM_MMAP_SUPPORTED, + /* Request a range and offset to be mapped; + * while mapped, you MUST NOT use any read/write functions + * on the stream (win9x compatibility) */ + PHP_STREAM_MMAP_MAP_RANGE, + /* Unmap the last range that was mapped for the stream */ + PHP_STREAM_MMAP_UNMAP +} php_stream_mmap_operation_t; + +typedef enum { + PHP_STREAM_MAP_MODE_READONLY, + PHP_STREAM_MAP_MODE_READWRITE, + PHP_STREAM_MAP_MODE_SHARED_READONLY, + PHP_STREAM_MAP_MODE_SHARED_READWRITE +} php_stream_mmap_access_t; + +typedef struct { + /* requested offset and length. + * If length is 0, the whole file is mapped */ + size_t offset; + size_t length; + + php_stream_mmap_access_t mode; + + /* returned mapped address */ + char *mapped; + +} php_stream_mmap_range; + +#define PHP_STREAM_MMAP_ALL 0 + +#define php_stream_mmap_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_SUPPORTED, NULL TSRMLS_CC) == 0 ? 1 : 0) + +/* Returns 1 if the stream in its current state can be memory mapped, + * 0 otherwise */ +#define php_stream_mmap_possible(stream) (!php_stream_is_filtered((stream)) && php_stream_mmap_supported((stream))) + +BEGIN_EXTERN_C() +PHPAPI char *_php_stream_mmap_range(php_stream *stream, size_t offset, size_t length, php_stream_mmap_operation_t mode, size_t *mapped_len TSRMLS_DC); +#define php_stream_mmap_range(stream, offset, length, mode, mapped_len) _php_stream_mmap_range((stream), (offset), (length), (mode), (mapped_len) TSRMLS_CC) + +/* un-maps the last mapped range */ +PHPAPI int _php_stream_mmap_unmap(php_stream *stream TSRMLS_DC); +#define php_stream_mmap_unmap(stream) _php_stream_mmap_unmap((stream) TSRMLS_CC) + +PHPAPI int _php_stream_mmap_unmap_ex(php_stream *stream, off_t readden TSRMLS_DC); +#define php_stream_mmap_unmap_ex(stream, readden) _php_stream_mmap_unmap_ex((stream), (readden) TSRMLS_CC) +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/php_stream_plain_wrapper.h b/main/streams/php_stream_plain_wrapper.h new file mode 100644 index 0000000..d88b30c --- /dev/null +++ b/main/streams/php_stream_plain_wrapper.h @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* definitions for the plain files wrapper */ + +/* operations for a plain file; use the php_stream_fopen_XXX funcs below */ +PHPAPI extern php_stream_ops php_stream_stdio_ops; +PHPAPI extern php_stream_wrapper php_plain_files_wrapper; + +BEGIN_EXTERN_C() + +/* like fopen, but returns a stream */ +PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC); +#define php_stream_fopen(filename, mode, opened) _php_stream_fopen((filename), (mode), (opened), 0 STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_with_path(filename, mode, path, opened) _php_stream_fopen_with_path((filename), (mode), (path), (opened), 0 STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_from_file(file, mode) _php_stream_fopen_from_file((file), (mode) STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_from_fd(fd, mode, persistent_id) _php_stream_fopen_from_fd((fd), (mode), (persistent_id) STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_from_pipe(file, mode) _php_stream_fopen_from_pipe((file), (mode) STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_tmpfile() _php_stream_fopen_tmpfile(0 STREAMS_CC TSRMLS_CC) + +PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC); +#define php_stream_fopen_temporary_file(dir, pfx, opened_path) _php_stream_fopen_temporary_file((dir), (pfx), (opened_path) STREAMS_CC TSRMLS_CC) + +/* This is a utility API for extensions that are opening a stream, converting it + * to a FILE* and then closing it again. Be warned that fileno() on the result + * will most likely fail on systems with fopencookie. */ +PHPAPI FILE * _php_stream_open_wrapper_as_file(char * path, char * mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); +#define php_stream_open_wrapper_as_file(path, mode, options, opened_path) _php_stream_open_wrapper_as_file((path), (mode), (options), (opened_path) STREAMS_CC TSRMLS_CC) + +END_EXTERN_C() + +/* + * 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/streams/php_stream_transport.h b/main/streams/php_stream_transport.h new file mode 100644 index 0000000..c2d9110 --- /dev/null +++ b/main/streams/php_stream_transport.h @@ -0,0 +1,211 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ +#ifdef PHP_WIN32 +#include "config.w32.h" +#include +#endif + +#if HAVE_SYS_SOCKET_H +# include +#endif + +typedef php_stream *(php_stream_transport_factory_func)(const char *proto, long protolen, + char *resourcename, long resourcenamelen, + const char *persistent_id, int options, int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC TSRMLS_DC); +typedef php_stream_transport_factory_func *php_stream_transport_factory; + +BEGIN_EXTERN_C() +PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC); +PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC); + +#define STREAM_XPORT_CLIENT 0 +#define STREAM_XPORT_SERVER 1 + +#define STREAM_XPORT_CONNECT 2 +#define STREAM_XPORT_BIND 4 +#define STREAM_XPORT_LISTEN 8 +#define STREAM_XPORT_CONNECT_ASYNC 16 + +/* Open a client or server socket connection */ +PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options, + int flags, const char *persistent_id, + struct timeval *timeout, + php_stream_context *context, + char **error_string, + int *error_code + STREAMS_DC TSRMLS_DC); + +#define php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode) \ + _php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode STREAMS_CC TSRMLS_CC) + +/* Bind the stream to a local address */ +PHPAPI int php_stream_xport_bind(php_stream *stream, + const char *name, long namelen, + char **error_text + TSRMLS_DC); + +/* Connect to a remote address */ +PHPAPI int php_stream_xport_connect(php_stream *stream, + const char *name, long namelen, + int asynchronous, + struct timeval *timeout, + char **error_text, + int *error_code + TSRMLS_DC); + +/* Prepare to listen */ +PHPAPI int php_stream_xport_listen(php_stream *stream, + int backlog, + char **error_text + TSRMLS_DC); + +/* Get the next client and their address as a string, or the underlying address + * structure. You must efree either of these if you request them */ +PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client, + char **textaddr, int *textaddrlen, + void **addr, socklen_t *addrlen, + struct timeval *timeout, + char **error_text + TSRMLS_DC); + +/* Get the name of either the socket or it's peer */ +PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer, + char **textaddr, int *textaddrlen, + void **addr, socklen_t *addrlen + TSRMLS_DC); + +enum php_stream_xport_send_recv_flags { + STREAM_OOB = 1, + STREAM_PEEK = 2 +}; + +/* Similar to recv() system call; read data from the stream, optionally + * peeking, optionally retrieving OOB data */ +PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen, + long flags, void **addr, socklen_t *addrlen, + char **textaddr, int *textaddrlen TSRMLS_DC); + +/* Similar to send() system call; send data to the stream, optionally + * sending it as OOB data */ +PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen, + long flags, void *addr, socklen_t addrlen TSRMLS_DC); + +typedef enum { + STREAM_SHUT_RD, + STREAM_SHUT_WR, + STREAM_SHUT_RDWR +} stream_shutdown_t; + +/* Similar to shutdown() system call; shut down part of a full-duplex + * connection */ +PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC); +END_EXTERN_C() + + +/* Structure definition for the set_option interface that the above functions wrap */ + +typedef struct _php_stream_xport_param { + enum { + STREAM_XPORT_OP_BIND, STREAM_XPORT_OP_CONNECT, + STREAM_XPORT_OP_LISTEN, STREAM_XPORT_OP_ACCEPT, + STREAM_XPORT_OP_CONNECT_ASYNC, + STREAM_XPORT_OP_GET_NAME, + STREAM_XPORT_OP_GET_PEER_NAME, + STREAM_XPORT_OP_RECV, + STREAM_XPORT_OP_SEND, + STREAM_XPORT_OP_SHUTDOWN + } op; + unsigned int want_addr:1; + unsigned int want_textaddr:1; + unsigned int want_errortext:1; + unsigned int how:2; + + struct { + char *name; + long namelen; + int backlog; + struct timeval *timeout; + struct sockaddr *addr; + socklen_t addrlen; + char *buf; + size_t buflen; + long flags; + } inputs; + struct { + php_stream *client; + int returncode; + struct sockaddr *addr; + socklen_t addrlen; + char *textaddr; + long textaddrlen; + + char *error_text; + int error_code; + } outputs; +} php_stream_xport_param; + + +/* These functions provide crypto support on the underlying transport */ +typedef enum { + STREAM_CRYPTO_METHOD_SSLv2_CLIENT, + STREAM_CRYPTO_METHOD_SSLv3_CLIENT, + STREAM_CRYPTO_METHOD_SSLv23_CLIENT, + STREAM_CRYPTO_METHOD_TLS_CLIENT, + STREAM_CRYPTO_METHOD_SSLv2_SERVER, + STREAM_CRYPTO_METHOD_SSLv3_SERVER, + STREAM_CRYPTO_METHOD_SSLv23_SERVER, + STREAM_CRYPTO_METHOD_TLS_SERVER +} php_stream_xport_crypt_method_t; + +BEGIN_EXTERN_C() +PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC); +PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC); +END_EXTERN_C() + +typedef struct _php_stream_xport_crypto_param { + enum { + STREAM_XPORT_CRYPTO_OP_SETUP, + STREAM_XPORT_CRYPTO_OP_ENABLE + } op; + struct { + int activate; + php_stream_xport_crypt_method_t method; + php_stream *session; + } inputs; + struct { + int returncode; + } outputs; +} php_stream_xport_crypto_param; + +BEGIN_EXTERN_C() +PHPAPI HashTable *php_stream_xport_get_hash(void); +PHPAPI php_stream_transport_factory_func php_stream_generic_socket_factory; +END_EXTERN_C() + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/php_stream_userspace.h b/main/streams/php_stream_userspace.h new file mode 100644 index 0000000..2830dd0 --- /dev/null +++ b/main/streams/php_stream_userspace.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + + +/* for user-space streams */ +PHPAPI extern php_stream_ops php_stream_userspace_ops; +PHPAPI extern php_stream_ops php_stream_userspace_dir_ops; +#define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops +#define PHP_STREAM_IS_USERSPACE_DIR &php_stream_userspace_dir_ops + +/* + * 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/streams/php_streams_int.h b/main/streams/php_streams_int.h new file mode 100644 index 0000000..daae2b8 --- /dev/null +++ b/main/streams/php_streams_int.h @@ -0,0 +1,71 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + + +#if ZEND_DEBUG + +#define emalloc_rel_orig(size) \ + ( __php_stream_call_depth == 0 \ + ? _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ + : _emalloc((size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) + +#define erealloc_rel_orig(ptr, size) \ + ( __php_stream_call_depth == 0 \ + ? _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_RELAY_CC) \ + : _erealloc((ptr), (size), 0 ZEND_FILE_LINE_CC ZEND_FILE_LINE_ORIG_RELAY_CC) ) + +#define pemalloc_rel_orig(size, persistent) ((persistent) ? malloc((size)) : emalloc_rel_orig((size))) +#define perealloc_rel_orig(ptr, size, persistent) ((persistent) ? realloc((ptr), (size)) : erealloc_rel_orig((ptr), (size))) +#else +# define pemalloc_rel_orig(size, persistent) pemalloc((size), (persistent)) +# define perealloc_rel_orig(ptr, size, persistent) perealloc((ptr), (size), (persistent)) +# define emalloc_rel_orig(size) emalloc((size)) +#endif + +#define STREAM_DEBUG 0 +#define STREAM_WRAPPER_PLAIN_FILES ((php_stream_wrapper*)-1) + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif + +#define CHUNK_SIZE 8192 + +#ifdef PHP_WIN32 +# ifdef EWOULDBLOCK +# undef EWOULDBLOCK +# endif +# define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#endif + +/* This functions transforms the first char to 'w' if it's not 'r', 'a' or 'w' + * and strips any subsequent chars except '+' and 'b'. + * Use this to sanitize stream->mode if you call e.g. fdopen, fopencookie or + * any other function that expects standard modes and you allow non-standard + * ones. result should be a char[5]. */ +void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *result); + +void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC); +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC); + diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c new file mode 100644 index 0000000..4ee8150 --- /dev/null +++ b/main/streams/plain_wrapper.c @@ -0,0 +1,1505 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include "ext/standard/flock_compat.h" +#include "ext/standard/php_filestat.h" +#include +#include +#if HAVE_SYS_WAIT_H +#include +#endif +#if HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif +#include "SAPI.h" + +#include "php_streams_int.h" +#ifdef PHP_WIN32 +# include "win32/winutil.h" +#endif + +#define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC TSRMLS_CC) +#define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC TSRMLS_CC) +#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) +{ + int flags; + + switch (mode[0]) { + case 'r': + flags = 0; + break; + case 'w': + flags = O_TRUNC|O_CREAT; + break; + case 'a': + flags = O_CREAT|O_APPEND; + break; + case 'x': + flags = O_CREAT|O_EXCL; + break; + case 'c': + flags = O_CREAT; + break; + default: + /* unknown mode */ + return FAILURE; + } +#if defined(O_NONBLOCK) + if (strchr(mode, 'n')) { + flags |= O_NONBLOCK; + } +#endif + if (strchr(mode, '+')) { + flags |= O_RDWR; + } else if (flags) { + flags |= O_WRONLY; + } else { + flags |= O_RDONLY; + } + +#if defined(_O_TEXT) && defined(O_BINARY) + if (strchr(mode, 't')) { + flags |= _O_TEXT; + } else { + flags |= O_BINARY; + } +#endif + + *open_flags = flags; + return SUCCESS; +} + + +/* {{{ ------- STDIO stream implementation -------*/ + +typedef struct { + FILE *file; + int fd; /* underlying file descriptor */ + unsigned is_process_pipe:1; /* use pclose instead of fclose */ + 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 */ +#if HAVE_FLUSHIO + char last_op; +#endif + +#if HAVE_MMAP + char *last_mapped_addr; + size_t last_mapped_len; +#endif +#ifdef PHP_WIN32 + char *last_mapped_addr; + HANDLE file_mapping; +#endif + + struct stat sb; +} php_stdio_stream_data; +#define PHP_STDIOP_GET_FD(anfd, data) anfd = (data)->file ? fileno((data)->file) : (data)->fd + +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; + + return r; + } + return 0; +} + +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; + self->is_pipe = 0; + self->lock_flag = LOCK_UN; + 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; + self->is_pipe = 0; + self->lock_flag = LOCK_UN; + self->is_process_pipe = 0; + self->temp_file_name = NULL; + self->fd = fileno(file); + + return php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); +} + +PHPAPI php_stream *_php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path STREAMS_DC TSRMLS_DC) +{ + int fd = php_open_temporary_fd(dir, pfx, opened_path TSRMLS_CC); + + if (fd != -1) { + php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, "r+b", NULL); + if (stream) { + return stream; + } + close(fd); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); + + return NULL; + } + return NULL; +} + +PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC TSRMLS_DC) +{ + char *opened_path = NULL; + int fd = php_open_temporary_fd(NULL, "php", &opened_path TSRMLS_CC); + + if (fd != -1) { + php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, "r+b", NULL); + if (stream) { + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + stream->wrapper = &php_plain_files_wrapper; + stream->orig_path = estrdup(opened_path); + + self->temp_file_name = opened_path; + self->lock_flag = LOCK_UN; + + return stream; + } + close(fd); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to allocate stream"); + + return NULL; + } + return NULL; +} + +PHPAPI php_stream *_php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id); + + if (stream) { + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + +#ifdef S_ISFIFO + /* detect if this is a pipe */ + if (self->fd >= 0) { + self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0; + } +#elif defined(PHP_WIN32) + { + zend_uintptr_t handle = _get_osfhandle(self->fd); + + if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { + self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE; + } + } +#endif + + if (self->is_pipe) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + } else { + stream->position = lseek(self->fd, 0, SEEK_CUR); +#ifdef ESPIPE + if (stream->position == (off_t)-1 && errno == ESPIPE) { + stream->position = 0; + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + self->is_pipe = 1; + } +#endif + } + } + + return stream; +} + +PHPAPI php_stream *_php_stream_fopen_from_file(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = php_stream_fopen_from_file_int_rel(file, mode); + + if (stream) { + php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract; + +#ifdef S_ISFIFO + /* detect if this is a pipe */ + if (self->fd >= 0) { + self->is_pipe = (do_fstat(self, 0) == 0 && S_ISFIFO(self->sb.st_mode)) ? 1 : 0; + } +#elif defined(PHP_WIN32) + { + zend_uintptr_t handle = _get_osfhandle(self->fd); + + if (handle != (zend_uintptr_t)INVALID_HANDLE_VALUE) { + self->is_pipe = GetFileType((HANDLE)handle) == FILE_TYPE_PIPE; + } + } +#endif + + if (self->is_pipe) { + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + } else { + stream->position = ftell(file); + } + } + + return stream; +} + +PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STREAMS_DC TSRMLS_DC) +{ + php_stdio_stream_data *self; + php_stream *stream; + + self = emalloc_rel_orig(sizeof(*self)); + memset(self, 0, sizeof(*self)); + self->file = file; + self->is_pipe = 1; + self->lock_flag = LOCK_UN; + self->is_process_pipe = 1; + self->fd = fileno(file); + self->temp_file_name = NULL; + + stream = php_stream_alloc_rel(&php_stream_stdio_ops, self, 0, mode); + stream->flags |= PHP_STREAM_FLAG_NO_SEEK; + return stream; +} + +static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + + if (data->fd >= 0) { + int bytes_written = write(data->fd, buf, count); + if (bytes_written < 0) return 0; + return (size_t) bytes_written; + } else { + +#if HAVE_FLUSHIO + if (!data->is_pipe && data->last_op == 'r') { + fseek(data->file, 0, SEEK_CUR); + } + data->last_op = 'w'; +#endif + + return fwrite(buf, 1, count, data->file); + } +} + +static size_t php_stdiop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + size_t ret; + + assert(data != NULL); + + if (data->fd >= 0) { + ret = read(data->fd, buf, count); + + if (ret == (size_t)-1 && errno == EINTR) { + /* Read was interrupted, retry once, + If read still fails, giveup with feof==0 + 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') + fseek(data->file, 0, SEEK_CUR); + data->last_op = 'r'; +#endif + + ret = fread(buf, 1, count, data->file); + + stream->eof = feof(data->file); + } + return ret; +} + +static int php_stdiop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + int ret; + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + +#if HAVE_MMAP + if (data->last_mapped_addr) { + munmap(data->last_mapped_addr, data->last_mapped_len); + data->last_mapped_addr = NULL; + } +#elif defined(PHP_WIN32) + if (data->last_mapped_addr) { + UnmapViewOfFile(data->last_mapped_addr); + data->last_mapped_addr = NULL; + } + if (data->file_mapping) { + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + } +#endif + + if (close_handle) { + if (data->file) { + if (data->is_process_pipe) { + errno = 0; + ret = pclose(data->file); + +#if HAVE_SYS_WAIT_H + if (WIFEXITED(ret)) { + ret = WEXITSTATUS(ret); + } +#endif + } else { + ret = fclose(data->file); + data->file = NULL; + } + } else if (data->fd != -1) { + ret = close(data->fd); + data->fd = -1; + } else { + return 0; /* everything should be closed already -> success */ + } + if (data->temp_file_name) { + unlink(data->temp_file_name); + /* temporary streams are never persistent */ + efree(data->temp_file_name); + data->temp_file_name = NULL; + } + } else { + ret = 0; + data->file = NULL; + data->fd = -1; + } + + pefree(data, stream->is_persistent); + + return ret; +} + +static int php_stdiop_flush(php_stream *stream TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + + assert(data != NULL); + + /* + * stdio buffers data in user land. By calling fflush(3), this + * data is send to the kernel using write(2). fsync'ing is + * something completely different. + */ + if (data->file) { + return fflush(data->file); + } + return 0; +} + +static int php_stdiop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; + int ret; + + assert(data != NULL); + + if (data->is_pipe) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot seek on a pipe"); + return -1; + } + + 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); + return ret; + } +} + +static int php_stdiop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + int fd; + 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 */ + + switch (castas) { + case PHP_STREAM_AS_STDIO: + if (ret) { + + if (data->file == NULL) { + /* we were opened as a plain file descriptor, so we + * need fdopen now */ + char fixed_mode[5]; + php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode); + data->file = fdopen(data->fd, fixed_mode); + if (data->file == NULL) { + return FAILURE; + } + } + + *(FILE**)ret = data->file; + data->fd = -1; + } + return SUCCESS; + + case PHP_STREAM_AS_FD_FOR_SELECT: + PHP_STDIOP_GET_FD(fd, data); + if (fd < 0) { + return FAILURE; + } + if (ret) { + *(int*)ret = fd; + } + return SUCCESS; + + case PHP_STREAM_AS_FD: + PHP_STDIOP_GET_FD(fd, data); + + if (fd < 0) { + return FAILURE; + } + if (data->file) { + fflush(data->file); + } + if (ret) { + *(int*)ret = fd; + } + return SUCCESS; + default: + return FAILURE; + } +} + +static int php_stdiop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + int ret; + php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; + + assert(data != NULL); + + ret = do_fstat(data, 1); + memcpy(&ssb->sb, &data->sb, sizeof(ssb->sb)); + return ret; +} + +static int php_stdiop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + php_stdio_stream_data *data = (php_stdio_stream_data*) stream->abstract; + size_t size; + int fd; +#ifdef O_NONBLOCK + /* FIXME: make this work for win32 */ + int flags; + int oldval; +#endif + + PHP_STDIOP_GET_FD(fd, data); + + switch(option) { + case PHP_STREAM_OPTION_BLOCKING: + if (fd == -1) + return -1; +#ifdef O_NONBLOCK + flags = fcntl(fd, F_GETFL, 0); + oldval = (flags & O_NONBLOCK) ? 0 : 1; + if (value) + 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 + size = BUFSIZ; + + switch(value) { + case PHP_STREAM_BUFFER_NONE: + return setvbuf(data->file, NULL, _IONBF, 0); + + case PHP_STREAM_BUFFER_LINE: + return setvbuf(data->file, NULL, _IOLBF, size); + + case PHP_STREAM_BUFFER_FULL: + return setvbuf(data->file, NULL, _IOFBF, size); + + default: + return -1; + } + break; + + case PHP_STREAM_OPTION_LOCKING: + if (fd == -1) { + return -1; + } + + if ((zend_uintptr_t) ptrparam == PHP_STREAM_LOCK_SUPPORTED) { + return 0; + } + + if (!flock(fd, value)) { + data->lock_flag = value; + return 0; + } else { + return -1; + } + break; + + case PHP_STREAM_OPTION_MMAP_API: +#if HAVE_MMAP + { + 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; + + case PHP_STREAM_MMAP_MAP_RANGE: + do_fstat(data, 1); + if (range->length == 0 && range->offset > 0 && range->offset < data->sb.st_size) { + range->length = data->sb.st_size - range->offset; + } + if (range->length == 0 || range->length > data->sb.st_size) { + range->length = data->sb.st_size; + } + if (range->offset >= data->sb.st_size) { + range->offset = data->sb.st_size; + range->length = 0; + } + switch (range->mode) { + case PHP_STREAM_MAP_MODE_READONLY: + prot = PROT_READ; + flags = MAP_PRIVATE; + break; + case PHP_STREAM_MAP_MODE_READWRITE: + prot = PROT_READ | PROT_WRITE; + flags = MAP_PRIVATE; + break; + case PHP_STREAM_MAP_MODE_SHARED_READONLY: + prot = PROT_READ; + flags = MAP_SHARED; + break; + case PHP_STREAM_MAP_MODE_SHARED_READWRITE: + prot = PROT_READ | PROT_WRITE; + flags = MAP_SHARED; + break; + default: + return PHP_STREAM_OPTION_RETURN_ERR; + } + range->mapped = (char*)mmap(NULL, range->length, prot, flags, fd, range->offset); + if (range->mapped == (char*)MAP_FAILED) { + range->mapped = NULL; + return PHP_STREAM_OPTION_RETURN_ERR; + } + /* remember the mapping */ + data->last_mapped_addr = range->mapped; + data->last_mapped_len = range->length; + return PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_MMAP_UNMAP: + if (data->last_mapped_addr) { + munmap(data->last_mapped_addr, data->last_mapped_len); + data->last_mapped_addr = NULL; + + return PHP_STREAM_OPTION_RETURN_OK; + } + return PHP_STREAM_OPTION_RETURN_ERR; + } + } +#elif defined(PHP_WIN32) + { + php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam; + HANDLE hfile = (HANDLE)_get_osfhandle(fd); + DWORD prot, acc, loffs = 0, delta = 0; + + switch (value) { + case PHP_STREAM_MMAP_SUPPORTED: + return hfile == INVALID_HANDLE_VALUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_MMAP_MAP_RANGE: + switch (range->mode) { + case PHP_STREAM_MAP_MODE_READONLY: + prot = PAGE_READONLY; + acc = FILE_MAP_READ; + break; + case PHP_STREAM_MAP_MODE_READWRITE: + prot = PAGE_READWRITE; + acc = FILE_MAP_READ | FILE_MAP_WRITE; + break; + case PHP_STREAM_MAP_MODE_SHARED_READONLY: + prot = PAGE_READONLY; + acc = FILE_MAP_READ; + /* TODO: we should assign a name for the mapping */ + break; + case PHP_STREAM_MAP_MODE_SHARED_READWRITE: + prot = PAGE_READWRITE; + acc = FILE_MAP_READ | FILE_MAP_WRITE; + /* TODO: we should assign a name for the mapping */ + break; + default: + return PHP_STREAM_OPTION_RETURN_ERR; + } + + /* create a mapping capable of viewing the whole file (this costs no real resources) */ + data->file_mapping = CreateFileMapping(hfile, NULL, prot, 0, 0, NULL); + + if (data->file_mapping == NULL) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + + size = GetFileSize(hfile, NULL); + if (range->length == 0 && range->offset > 0 && range->offset < size) { + range->length = size - range->offset; + } + if (range->length == 0 || range->length > size) { + range->length = size; + } + if (range->offset >= size) { + range->offset = size; + range->length = 0; + } + + /* figure out how big a chunk to map to be able to view the part that we need */ + if (range->offset != 0) { + SYSTEM_INFO info; + DWORD gran; + + GetSystemInfo(&info); + gran = info.dwAllocationGranularity; + loffs = (range->offset / gran) * gran; + delta = range->offset - loffs; + } + + data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta); + + if (data->last_mapped_addr) { + /* give them back the address of the start offset they requested */ + range->mapped = data->last_mapped_addr + delta; + return PHP_STREAM_OPTION_RETURN_OK; + } + + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + + return PHP_STREAM_OPTION_RETURN_ERR; + + case PHP_STREAM_MMAP_UNMAP: + if (data->last_mapped_addr) { + UnmapViewOfFile(data->last_mapped_addr); + data->last_mapped_addr = NULL; + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + return PHP_STREAM_OPTION_RETURN_OK; + } + return PHP_STREAM_OPTION_RETURN_ERR; + + default: + return PHP_STREAM_OPTION_RETURN_ERR; + } + } + +#endif + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + + case PHP_STREAM_OPTION_TRUNCATE_API: + switch (value) { + case PHP_STREAM_TRUNCATE_SUPPORTED: + return fd == -1 ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_TRUNCATE_SET_SIZE: { + ptrdiff_t new_size = *(ptrdiff_t*)ptrparam; + if (new_size < 0) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + return ftruncate(fd, new_size) == 0 ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + } + + default: + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + } +} + +PHPAPI php_stream_ops php_stream_stdio_ops = { + php_stdiop_write, php_stdiop_read, + php_stdiop_close, php_stdiop_flush, + "STDIO", + php_stdiop_seek, + php_stdiop_cast, + php_stdiop_stat, + php_stdiop_set_option +}; +/* }}} */ + +/* {{{ plain files opendir/readdir implementation */ +static size_t php_plain_files_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + DIR *dir = (DIR*)stream->abstract; + /* avoid libc5 readdir problems */ + char entry[sizeof(struct dirent)+MAXPATHLEN]; + struct dirent *result = (struct dirent *)&entry; + php_stream_dirent *ent = (php_stream_dirent*)buf; + + /* avoid problems if someone mis-uses the stream */ + if (count != sizeof(php_stream_dirent)) + return 0; + + if (php_readdir_r(dir, (struct dirent *)entry, &result) == 0 && result) { + PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); + return sizeof(php_stream_dirent); + } + return 0; +} + +static int php_plain_files_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + return closedir((DIR *)stream->abstract); +} + +static int php_plain_files_dirstream_rewind(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) +{ + rewinddir((DIR *)stream->abstract); + return 0; +} + +static php_stream_ops php_plain_files_dirstream_ops = { + NULL, php_plain_files_dirstream_read, + php_plain_files_dirstream_close, NULL, + "dir", + php_plain_files_dirstream_rewind, + NULL, /* cast */ + NULL, /* stat */ + NULL /* set_option */ +}; + +static php_stream *php_plain_files_dir_opener(php_stream_wrapper *wrapper, char *path, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + DIR *dir = NULL; + php_stream *stream = NULL; + +#ifdef HAVE_GLOB + if (options & STREAM_USE_GLOB_DIR_OPEN) { + return php_glob_stream_wrapper.wops->dir_opener(&php_glob_stream_wrapper, path, mode, options, opened_path, context STREAMS_REL_CC TSRMLS_CC); + } +#endif + + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path TSRMLS_CC)) { + return NULL; + } + + dir = VCWD_OPENDIR(path); + +#ifdef PHP_WIN32 + if (!dir) { + php_win32_docref2_from_error(GetLastError(), path, path TSRMLS_CC); + } + + if (dir && dir->finished) { + closedir(dir); + dir = NULL; + } +#endif + if (dir) { + stream = php_stream_alloc(&php_plain_files_dirstream_ops, dir, 0, mode); + if (stream == NULL) + closedir(dir); + } + + return stream; +} +/* }}} */ + +/* {{{ php_stream_fopen */ +PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, char **opened_path, int options STREAMS_DC TSRMLS_DC) +{ + char *realpath = NULL; + int open_flags; + int fd; + php_stream *ret; + int persistent = options & STREAM_OPEN_PERSISTENT; + char *persistent_id = NULL; + + if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { + if (options & REPORT_ERRORS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "`%s' is not a valid mode for fopen", mode); + } + return NULL; + } + + if (options & STREAM_ASSUME_REALPATH) { + realpath = estrdup(filename); + } else { + if ((realpath = expand_filepath(filename, NULL TSRMLS_CC)) == NULL) { + return NULL; + } + } + + if (persistent) { + spprintf(&persistent_id, 0, "streams_stdio_%d_%s", open_flags, realpath); + switch (php_stream_from_persistent_id(persistent_id, &ret TSRMLS_CC)) { + case PHP_STREAM_PERSISTENT_SUCCESS: + if (opened_path) { + *opened_path = realpath; + realpath = NULL; + } + /* fall through */ + + case PHP_STREAM_PERSISTENT_FAILURE: + if (realpath) { + efree(realpath); + } + efree(persistent_id);; + return ret; + } + } + + fd = open(realpath, open_flags, 0666); + + if (fd != -1) { + + if (options & STREAM_OPEN_FOR_INCLUDE) { + ret = php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id); + } else { + ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id); + } + + if (ret) { + if (opened_path) { + *opened_path = realpath; + realpath = NULL; + } + if (realpath) { + efree(realpath); + } + if (persistent_id) { + efree(persistent_id); + } + + /* WIN32 always set ISREG flag */ +#ifndef PHP_WIN32 + /* sanity checks for include/require. + * We check these after opening the stream, so that we save + * on fstat() syscalls */ + if (options & STREAM_OPEN_FOR_INCLUDE) { + php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract; + int r; + + r = do_fstat(self, 0); + if ((r == 0 && !S_ISREG(self->sb.st_mode))) { + if (opened_path) { + efree(*opened_path); + *opened_path = NULL; + } + php_stream_close(ret); + return NULL; + } + } +#endif + + return ret; + } + close(fd); + } + efree(realpath); + if (persistent_id) { + efree(persistent_id); + } + return NULL; +} +/* }}} */ + + +static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, char *path, char *mode, + int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(path TSRMLS_CC)) { + 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) +{ + + 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)) { + return -1; + } + +#ifdef PHP_WIN32 + if (EG(windows_version_info).dwMajorVersion >= 5) { + if (flags & PHP_STREAM_URL_STAT_LINK) { + return VCWD_LSTAT(url, &ssb->sb); + } + } +#else +# ifdef HAVE_SYMLINK + if (flags & PHP_STREAM_URL_STAT_LINK) { + return VCWD_LSTAT(url, &ssb->sb); + } else +# endif +#endif + return VCWD_STAT(url, &ssb->sb); +} + +static int php_plain_files_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ + char *p; + int ret; + + if ((p = strstr(url, "://")) != NULL) { + url = p + 3; + } + + if (php_check_open_basedir(url TSRMLS_CC)) { + return 0; + } + + ret = VCWD_UNLINK(url); + if (ret == -1) { + if (options & REPORT_ERRORS) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "%s", strerror(errno)); + } + return 0; + } + + /* Clear stat cache (and realpath cache) */ + php_clear_stat_cache(1, NULL, 0 TSRMLS_CC); + + return 1; +} + +static int php_plain_files_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) +{ + char *p; + int ret; + + if (!url_from || !url_to) { + return 0; + } + +#ifdef PHP_WIN32 + if (!php_win32_check_trailing_space(url_from, strlen(url_from))) { + php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to TSRMLS_CC); + return 0; + } + if (!php_win32_check_trailing_space(url_to, strlen(url_to))) { + php_win32_docref2_from_error(ERROR_INVALID_NAME, url_from, url_to TSRMLS_CC); + return 0; + } +#endif + + if ((p = strstr(url_from, "://")) != NULL) { + url_from = p + 3; + } + + if ((p = strstr(url_to, "://")) != NULL) { + url_to = p + 3; + } + + if (php_check_open_basedir(url_from TSRMLS_CC) || php_check_open_basedir(url_to TSRMLS_CC)) { + return 0; + } + + ret = VCWD_RENAME(url_from, url_to); + + if (ret == -1) { +#ifndef PHP_WIN32 +# ifdef EXDEV + if (errno == EXDEV) { + struct stat sb; + if (php_copy_file(url_from, url_to TSRMLS_CC) == SUCCESS) { + if (VCWD_STAT(url_from, &sb) == 0) { +# if !defined(TSRM_WIN32) && !defined(NETWARE) + if (VCWD_CHMOD(url_to, sb.st_mode)) { + if (errno == EPERM) { + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); + VCWD_UNLINK(url_from); + return 1; + } + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); + return 0; + } + if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { + if (errno == EPERM) { + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); + VCWD_UNLINK(url_from); + return 1; + } + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); + return 0; + } +# endif + VCWD_UNLINK(url_from); + return 1; + } + } + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); + return 0; + } +# endif +#endif + +#ifdef PHP_WIN32 + php_win32_docref2_from_error(GetLastError(), url_from, url_to TSRMLS_CC); +#else + php_error_docref2(NULL TSRMLS_CC, url_from, url_to, E_WARNING, "%s", strerror(errno)); +#endif + return 0; + } + + /* Clear stat cache (and realpath cache) */ + php_clear_stat_cache(1, NULL, 0 TSRMLS_CC); + + return 1; +} + +static int php_plain_files_mkdir(php_stream_wrapper *wrapper, char *dir, int mode, int options, php_stream_context *context TSRMLS_DC) +{ + int ret, recursive = options & PHP_STREAM_MKDIR_RECURSIVE; + char *p; + + if ((p = strstr(dir, "://")) != NULL) { + 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; + struct stat sb; + int dir_len = strlen(dir); + int offset = 0; + char buf[MAXPATHLEN]; + + 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; + } + + e = buf + strlen(buf); + + if ((p = memchr(buf, DEFAULT_SLASH, dir_len))) { + offset = p - buf + 1; + } + + if (p && dir_len == 1) { + /* buf == "DEFAULT_SLASH" */ + } + else { + /* find a top level directory we need to create */ + while ( (p = strrchr(buf + offset, DEFAULT_SLASH)) || (offset != 1 && (p = strrchr(buf, DEFAULT_SLASH))) ) { + int n = 0; + + *p = '\0'; + while (p > buf && *(p-1) == DEFAULT_SLASH) { + ++n; + --p; + *p = '\0'; + } + if (VCWD_STAT(buf, &sb) == 0) { + while (1) { + *p = DEFAULT_SLASH; + if (!n) break; + --n; + ++p; + } + break; + } + } + } + + if (p == buf) { + ret = php_mkdir(dir, mode TSRMLS_CC); + } else if (!(ret = php_mkdir(buf, mode TSRMLS_CC))) { + if (!p) { + p = buf; + } + /* create any needed directories if the creation of the 1st directory worked */ + while (++p != e) { + if (*p == '\0') { + *p = DEFAULT_SLASH; + if ((*(p+1) != '\0') && + (ret = VCWD_MKDIR(buf, (mode_t)mode)) < 0) { + if (options & REPORT_ERRORS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno)); + } + break; + } + } + } + } + } + if (ret < 0) { + /* Failure */ + return 0; + } else { + /* Success */ + return 1; + } +} + +static int php_plain_files_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ +#if PHP_WIN32 + int url_len = strlen(url); +#endif + if (php_check_open_basedir(url TSRMLS_CC)) { + return 0; + } + +#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 (VCWD_RMDIR(url) < 0) { + php_error_docref1(NULL TSRMLS_CC, url, E_WARNING, "%s", strerror(errno)); + return 0; + } + + /* Clear stat cache (and realpath cache) */ + php_clear_stat_cache(1, NULL, 0 TSRMLS_CC); + + 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, + NULL, + php_plain_files_url_stater, + php_plain_files_dir_opener, + "plainfile", + php_plain_files_unlink, + php_plain_files_rename, + php_plain_files_mkdir, + php_plain_files_rmdir, + php_plain_files_metadata +}; + +php_stream_wrapper php_plain_files_wrapper = { + &php_plain_files_wrapper_ops, + NULL, + 0 +}; + +/* {{{ php_stream_fopen_with_path */ +PHPAPI php_stream *_php_stream_fopen_with_path(char *filename, char *mode, char *path, char **opened_path, int options STREAMS_DC TSRMLS_DC) +{ + /* code ripped off from fopen_wrappers.c */ + char *pathbuf, *ptr, *end; + const char *exec_fname; + char trypath[MAXPATHLEN]; + php_stream *stream; + int path_length; + int filename_length; + int exec_fname_length; + + if (opened_path) { + *opened_path = NULL; + } + + if(!filename) { + return NULL; + } + + filename_length = strlen(filename); + + /* Relative path open */ + if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) { + /* further checks, we could have ....... filenames */ + ptr = filename + 1; + if (*ptr == '.') { + while (*(++ptr) == '.'); + if (!IS_SLASH(*ptr)) { /* not a relative path after all */ + goto not_relative_path; + } + } + + + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename TSRMLS_CC)) { + return NULL; + } + + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + +not_relative_path: + + /* Absolute path open */ + if (IS_ABSOLUTE_PATH(filename, filename_length)) { + + if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir(filename TSRMLS_CC)) { + return NULL; + } + + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + +#ifdef PHP_WIN32 + if (IS_SLASH(filename[0])) { + size_t cwd_len; + char *cwd; + 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; + } + + return php_stream_fopen_rel(trypath, mode, opened_path, options); + } +#endif + + if (!path || (path && !*path)) { + return php_stream_fopen_rel(filename, mode, opened_path, options); + } + + /* check in provided path */ + /* append the calling scripts' current working directory + * as a fall back case + */ + if (zend_is_executing(TSRMLS_C)) { + exec_fname = zend_get_executed_filename(TSRMLS_C); + exec_fname_length = strlen(exec_fname); + path_length = strlen(path); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if ((exec_fname && exec_fname[0] == '[') + || exec_fname_length<=0) { + /* [no active file] or no path */ + pathbuf = estrdup(path); + } else { + pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1); + memcpy(pathbuf, path, path_length); + pathbuf[path_length] = DEFAULT_DIR_SEPARATOR; + memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length); + pathbuf[path_length + exec_fname_length +1] = '\0'; + } + } else { + pathbuf = estrdup(path); + } + + ptr = pathbuf; + + while (ptr && *ptr) { + end = strchr(ptr, DEFAULT_DIR_SEPARATOR); + if (end != NULL) { + *end = '\0'; + end++; + } + if (*ptr == '\0') { + goto stream_skip; + } + 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 (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0 TSRMLS_CC)) { + goto stream_skip; + } + + stream = php_stream_fopen_rel(trypath, mode, opened_path, options); + if (stream) { + efree(pathbuf); + return stream; + } +stream_skip: + ptr = end; + } /* end provided path */ + + efree(pathbuf); + return NULL; + +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/streams.c b/main/streams/streams.c new file mode 100644 index 0000000..47d86b5 --- /dev/null +++ b/main/streams/streams.c @@ -0,0 +1,2397 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + | Borrowed code from: | + | Rasmus Lerdorf | + | Jim Winstead | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#define _GNU_SOURCE +#include "php.h" +#include "php_globals.h" +#include "php_network.h" +#include "php_open_temporary_file.h" +#include "ext/standard/file.h" +#include "ext/standard/basic_functions.h" /* for BG(mmap_file) (not strictly required) */ +#include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */ +#include +#include +#include "php_streams_int.h" + +/* {{{ resource and registration code */ +/* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */ +static HashTable url_stream_wrappers_hash; +static int le_stream = FAILURE; /* true global */ +static int le_pstream = FAILURE; /* true global */ +static int le_stream_filter = FAILURE; /* true global */ + +PHPAPI int php_file_le_stream(void) +{ + return le_stream; +} + +PHPAPI int php_file_le_pstream(void) +{ + return le_pstream; +} + +PHPAPI int php_file_le_stream_filter(void) +{ + return le_stream_filter; +} + +PHPAPI HashTable *_php_stream_get_url_stream_wrappers_hash(TSRMLS_D) +{ + return (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); +} + +PHPAPI HashTable *php_stream_get_url_stream_wrappers_hash_global(void) +{ + return &url_stream_wrappers_hash; +} + +static int _php_stream_release_context(zend_rsrc_list_entry *le, void *pContext TSRMLS_DC) +{ + if (le->ptr == pContext) { + return --le->refcount == 0; + } + return 0; +} + +static int forget_persistent_resource_id_numbers(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_stream *stream; + + if (Z_TYPE_P(rsrc) != le_pstream) { + return 0; + } + + stream = (php_stream*)rsrc->ptr; + +#if STREAM_DEBUG +fprintf(stderr, "forget_persistent: %s:%p\n", stream->ops->label, stream); +#endif + + stream->rsrc_id = FAILURE; + + if (stream->context) { + zend_hash_apply_with_argument(&EG(regular_list), + (apply_func_arg_t) _php_stream_release_context, + stream->context TSRMLS_CC); + stream->context = NULL; + } + + return 0; +} + +PHP_RSHUTDOWN_FUNCTION(streams) +{ + zend_hash_apply(&EG(persistent_list), (apply_func_t)forget_persistent_resource_id_numbers TSRMLS_CC); + 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; + + if (zend_hash_find(&EG(persistent_list), (char*)persistent_id, strlen(persistent_id)+1, (void*) &le) == SUCCESS) { + if (Z_TYPE_P(le) == le_pstream) { + if (stream) { + HashPosition pos; + zend_rsrc_list_entry *regentry; + ulong index = -1; /* intentional */ + + /* see if this persistent resource already has been loaded to the + * regular list; allowing the same resource in several entries in the + * regular list causes trouble (see bug #54623) */ + zend_hash_internal_pointer_reset_ex(&EG(regular_list), &pos); + while (zend_hash_get_current_data_ex(&EG(regular_list), + (void **)®entry, &pos) == SUCCESS) { + if (regentry->ptr == le->ptr) { + zend_hash_get_current_key_ex(&EG(regular_list), NULL, NULL, + &index, 0, &pos); + break; + } + zend_hash_move_forward_ex(&EG(regular_list), &pos); + } + + *stream = (php_stream*)le->ptr; + if (index == -1) { /* not found in regular list */ + le->refcount++; + (*stream)->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, *stream, le_pstream); + } else { + regentry->refcount++; + (*stream)->rsrc_id = index; + } + } + return PHP_STREAM_PERSISTENT_SUCCESS; + } + return PHP_STREAM_PERSISTENT_FAILURE; + } + return PHP_STREAM_PERSISTENT_NOT_EXIST; +} + +/* }}} */ + +static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper TSRMLS_DC) +{ + zend_llist *list = NULL; + if (!FG(wrapper_errors)) { + return NULL; + } else { + zend_hash_find(FG(wrapper_errors), (const char*)&wrapper, + sizeof wrapper, (void**)&list); + return list; + } +} + +/* {{{ wrapper error reporting */ +void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption TSRMLS_DC) +{ + char *tmp = estrdup(path); + char *msg; + int free_msg = 0; + + if (wrapper) { + zend_llist *err_list = php_get_wrapper_errors_list(wrapper TSRMLS_CC); + if (err_list) { + size_t l = 0; + int brlen; + int i; + int count = zend_llist_count(err_list); + const char *br; + const char **err_buf_p; + zend_llist_position pos; + + if (PG(html_errors)) { + brlen = 7; + br = "
\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; + err_buf_p; + err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { + l += strlen(*err_buf_p); + if (i < count - 1) { + l += brlen; + } + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; + err_buf_p; + err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, *err_buf_p); + if (i < count - 1) { + strcat(msg, br); + } + } + + free_msg = 1; + } else { + if (wrapper == &php_plain_files_wrapper) { + msg = strerror(errno); /* TODO: not ts on linux */ + } else { + msg = "operation failed"; + } + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "%s: %s", caption, msg); + efree(tmp); + if (free_msg) { + efree(msg); + } +} + +void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper TSRMLS_DC) +{ + if (wrapper && FG(wrapper_errors)) { + zend_hash_del(FG(wrapper_errors), (const char*)&wrapper, sizeof wrapper); + } +} + +static void wrapper_error_dtor(void *error) +{ + efree(*(char**)error); +} + +PHPAPI void php_stream_wrapper_log_error(php_stream_wrapper *wrapper, int options TSRMLS_DC, const char *fmt, ...) +{ + va_list args; + char *buffer = NULL; + + va_start(args, fmt); + vspprintf(&buffer, 0, fmt, args); + va_end(args); + + if (options & REPORT_ERRORS || wrapper == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buffer); + efree(buffer); + } else { + zend_llist *list = NULL; + if (!FG(wrapper_errors)) { + ALLOC_HASHTABLE(FG(wrapper_errors)); + zend_hash_init(FG(wrapper_errors), 8, NULL, + (dtor_func_t)zend_llist_destroy, 0); + } else { + zend_hash_find(FG(wrapper_errors), (const char*)&wrapper, + sizeof wrapper, (void**)&list); + } + + if (!list) { + zend_llist new_list; + zend_llist_init(&new_list, sizeof buffer, wrapper_error_dtor, 0); + zend_hash_update(FG(wrapper_errors), (const char*)&wrapper, + sizeof wrapper, &new_list, sizeof new_list, (void**)&list); + } + + /* append to linked list */ + zend_llist_add_element(list, &buffer); + } +} + + +/* }}} */ + +/* allocate a new stream for a particular ops */ +PHPAPI php_stream *_php_stream_alloc(php_stream_ops *ops, void *abstract, const char *persistent_id, const char *mode STREAMS_DC TSRMLS_DC) /* {{{ */ +{ + php_stream *ret; + + ret = (php_stream*) pemalloc_rel_orig(sizeof(php_stream), persistent_id ? 1 : 0); + + memset(ret, 0, sizeof(php_stream)); + + ret->readfilters.stream = ret; + ret->writefilters.stream = ret; + +#if STREAM_DEBUG +fprintf(stderr, "stream_alloc: %s:%p persistent=%s\n", ops->label, ret, persistent_id); +#endif + + ret->ops = ops; + ret->abstract = abstract; + ret->is_persistent = persistent_id ? 1 : 0; + ret->chunk_size = FG(def_chunk_size); + +#if ZEND_DEBUG + ret->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; + ret->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; +#endif + + if (FG(auto_detect_line_endings)) { + ret->flags |= PHP_STREAM_FLAG_DETECT_EOL; + } + + if (persistent_id) { + zend_rsrc_list_entry le; + + Z_TYPE(le) = le_pstream; + le.ptr = ret; + le.refcount = 0; + + if (FAILURE == zend_hash_update(&EG(persistent_list), (char *)persistent_id, + strlen(persistent_id) + 1, + (void *)&le, sizeof(le), NULL)) { + + pefree(ret, 1); + return NULL; + } + } + + 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 preserve_handle = close_options & PHP_STREAM_FREE_PRESERVE_HANDLE ? 1 : 0; + int release_cast = 1; + php_stream_context *context = NULL; + + /* on an resource list destruction, the context, another resource, may have + * already been freed (if it was created after the stream resource), so + * don't reference it */ + if (EG(active)) { + context = stream->context; + } + + if (stream->flags & PHP_STREAM_FLAG_NO_CLOSE) { + preserve_handle = 1; + } + +#if STREAM_DEBUG + { + 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 + + if (stream->in_free) { + /* 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* + * for include (or other similar) purposes. + * */ + if (preserve_handle) { + if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { + /* If the stream was fopencookied, we must NOT touch anything + * here, as the cookied stream relies on it all. + * Instead, mark the stream as OK to auto-clean */ + php_stream_auto_cleanup(stream); + stream->in_free--; + return 0; + } + /* otherwise, make sure that we don't close the FILE* from a cast */ + release_cast = 0; + } + +#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, + (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) { + /* 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. + * In any case, let's always completely delete it from the resource list, + * not only when PHP_STREAM_FREE_RELEASE_STREAM is set */ + while (zend_list_delete(stream->rsrc_id) == SUCCESS) {} + } + + /* Remove stream from any context link list */ + if (context && context->links) { + php_stream_context_del_link(context, stream); + } + + if (close_options & PHP_STREAM_FREE_CALL_DTOR) { + if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { + /* calling fclose on an fopencookied stream will ultimately + call this very same function. If we were called via fclose, + the cookie_closer unsets the fclose_stdiocast flags, so + we can be sure that we only reach here when PHP code calls + php_stream_free. + Lets let the cookie code clean it all up. + */ + stream->in_free = 0; + return fclose(stream->stdiocast); + } + + ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC); + stream->abstract = NULL; + + /* tidy up any FILE* that might have been fdopened */ + if (release_cast && stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FDOPEN && stream->stdiocast) { + fclose(stream->stdiocast); + stream->stdiocast = NULL; + stream->fclose_stdiocast = PHP_STREAM_FCLOSE_NONE; + } + } + + if (close_options & PHP_STREAM_FREE_RELEASE_STREAM) { + while (stream->readfilters.head) { + php_stream_filter_remove(stream->readfilters.head, 1 TSRMLS_CC); + } + while (stream->writefilters.head) { + php_stream_filter_remove(stream->writefilters.head, 1 TSRMLS_CC); + } + + if (stream->wrapper && stream->wrapper->wops && stream->wrapper->wops->stream_closer) { + stream->wrapper->wops->stream_closer(stream->wrapper, stream TSRMLS_CC); + stream->wrapper = NULL; + } + + if (stream->wrapperdata) { + zval_ptr_dtor(&stream->wrapperdata); + stream->wrapperdata = NULL; + } + + if (stream->readbuf) { + pefree(stream->readbuf, stream->is_persistent); + stream->readbuf = NULL; + } + + if (stream->is_persistent && (close_options & PHP_STREAM_FREE_PERSISTENT)) { + /* we don't work with *stream but need its value for comparison */ + zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) _php_stream_free_persistent, stream TSRMLS_CC); + } +#if ZEND_DEBUG + if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) { + /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it + * as leaked; it will log a warning, but lets help it out and display what kind + * of stream it was. */ + char *leakinfo; + spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path); + + if (stream->orig_path) { + pefree(stream->orig_path, stream->is_persistent); + stream->orig_path = NULL; + } + +# if defined(PHP_WIN32) + OutputDebugString(leakinfo); +# else + fprintf(stderr, "%s", leakinfo); +# endif + efree(leakinfo); + } else { + if (stream->orig_path) { + pefree(stream->orig_path, stream->is_persistent); + stream->orig_path = NULL; + } + + pefree(stream, stream->is_persistent); + } +#else + if (stream->orig_path) { + pefree(stream->orig_path, stream->is_persistent); + stream->orig_path = NULL; + } + + pefree(stream, stream->is_persistent); +#endif + } + + if (context) { + zend_list_delete(context->rsrc_id); + } + + return ret; +} +/* }}} */ + +/* {{{ generic stream operations */ + +static void php_stream_fill_read_buffer(php_stream *stream, size_t size TSRMLS_DC) +{ + /* allocate/fill the buffer */ + + if (stream->readfilters.head) { + char *chunk_buf; + int err_flag = 0; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + + /* Invalidate the existing cache, otherwise reads can fail, see note in + main/streams/filter.c::_php_stream_filter_append */ + stream->writepos = stream->readpos = 0; + + /* allocate a buffer for reading chunks */ + chunk_buf = emalloc(stream->chunk_size); + + while (!stream->eof && !err_flag && (stream->writepos - stream->readpos < (off_t)size)) { + size_t justread = 0; + int flags; + php_stream_bucket *bucket; + php_stream_filter_status_t status = PSFS_ERR_FATAL; + php_stream_filter *filter; + + /* read a chunk into a bucket */ + justread = stream->ops->read(stream, chunk_buf, stream->chunk_size TSRMLS_CC); + if (justread && justread != (size_t)-1) { + bucket = php_stream_bucket_new(stream, chunk_buf, justread, 0, 0 TSRMLS_CC); + + /* after this call, bucket is owned by the brigade */ + php_stream_bucket_append(brig_inp, bucket TSRMLS_CC); + + flags = PSFS_FLAG_NORMAL; + } else { + flags = stream->eof ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC; + } + + /* wind the handle... */ + for (filter = stream->readfilters.head; filter; filter = filter->next) { + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* we get here when the last filter in the chain has data to pass on. + * in this situation, we are passing the brig_in brigade into the + * stream read buffer */ + while (brig_inp->head) { + bucket = brig_inp->head; + /* grow buffer to hold this bucket + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < bucket->buflen) { + stream->readbuflen += bucket->buflen; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + memcpy(stream->readbuf + stream->writepos, bucket->buf, bucket->buflen); + stream->writepos += bucket->buflen; + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + break; + + case PSFS_FEED_ME: + /* when a filter needs feeding, there is no brig_out to deal with. + * we simply continue the loop; if the caller needs more data, + * we will read again, otherwise out job is done here */ + if (justread == 0) { + /* there is no data */ + err_flag = 1; + break; + } + continue; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further reads should fail. */ + err_flag = 1; + break; + } + + if (justread == 0 || justread == (size_t)-1) { + break; + } + } + + efree(chunk_buf); + + } else { + /* is there enough data in the buffer ? */ + if (stream->writepos - stream->readpos < (off_t)size) { + size_t justread = 0; + + /* reduce buffer memory consumption if possible, to avoid a realloc */ + if (stream->readbuf && stream->readbuflen - stream->writepos < stream->chunk_size) { + memmove(stream->readbuf, stream->readbuf + stream->readpos, stream->readbuflen - stream->readpos); + stream->writepos -= stream->readpos; + stream->readpos = 0; + } + + /* grow the buffer if required + * TODO: this can fail for persistent streams */ + if (stream->readbuflen - stream->writepos < stream->chunk_size) { + stream->readbuflen += stream->chunk_size; + stream->readbuf = perealloc(stream->readbuf, stream->readbuflen, + stream->is_persistent); + } + + justread = stream->ops->read(stream, stream->readbuf + stream->writepos, + stream->readbuflen - stream->writepos + TSRMLS_CC); + + if (justread != (size_t)-1) { + stream->writepos += justread; + } + } + } +} + +PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC) +{ + size_t toread = 0, didread = 0; + + while (size > 0) { + + /* take from the read buffer first. + * It is possible that a buffered stream was switched to non-buffered, so we + * drain the remainder of the buffer before using the "raw" read mode for + * the excess */ + if (stream->writepos > stream->readpos) { + + toread = stream->writepos - stream->readpos; + if (toread > size) { + toread = size; + } + + memcpy(buf, stream->readbuf + stream->readpos, toread); + stream->readpos += toread; + size -= toread; + buf += toread; + didread += toread; + } + + /* ignore eof here; the underlying state might have changed */ + if (size == 0) { + break; + } + + if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) { + toread = stream->ops->read(stream, buf, size TSRMLS_CC); + } else { + php_stream_fill_read_buffer(stream, size TSRMLS_CC); + + toread = stream->writepos - stream->readpos; + if (toread > size) { + toread = size; + } + + if (toread > 0) { + memcpy(buf, stream->readbuf + stream->readpos, toread); + stream->readpos += toread; + } + } + if (toread > 0) { + didread += toread; + buf += toread; + size -= toread; + } else { + /* EOF, or temporary end of data (for non-blocking mode). */ + break; + } + + /* just break anyway, to avoid greedy read */ + if (stream->wrapper != &php_plain_files_wrapper) { + break; + } + } + + if (didread > 0) { + stream->position += didread; + } + + return didread; +} + +PHPAPI int _php_stream_eof(php_stream *stream TSRMLS_DC) +{ + /* if there is data in the buffer, it's not EOF */ + if (stream->writepos - stream->readpos > 0) { + return 0; + } + + /* use the configured timeout when checking eof */ + if (!stream->eof && PHP_STREAM_OPTION_RETURN_ERR == + php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, + 0, NULL)) { + stream->eof = 1; + } + + return stream->eof; +} + +PHPAPI int _php_stream_putc(php_stream *stream, int c TSRMLS_DC) +{ + unsigned char buf = c; + + if (php_stream_write(stream, &buf, 1) > 0) { + return 1; + } + return EOF; +} + +PHPAPI int _php_stream_getc(php_stream *stream TSRMLS_DC) +{ + char buf; + + if (php_stream_read(stream, &buf, 1) > 0) { + return buf & 0xff; + } + return EOF; +} + +PHPAPI int _php_stream_puts(php_stream *stream, char *buf TSRMLS_DC) +{ + int len; + char newline[2] = "\n"; /* is this OK for Win? */ + len = strlen(buf); + + if (len > 0 && php_stream_write(stream, buf, len) && php_stream_write(stream, newline, 1)) { + return 1; + } + return 0; +} + +PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + memset(ssb, 0, sizeof(*ssb)); + + /* if the stream was wrapped, allow the wrapper to stat it */ + if (stream->wrapper && stream->wrapper->wops->stream_stat != NULL) { + return stream->wrapper->wops->stream_stat(stream->wrapper, stream, ssb TSRMLS_CC); + } + + /* if the stream doesn't directly support stat-ing, return with failure. + * We could try and emulate this by casting to a FD and fstat-ing it, + * but since the fd might not represent the actual underlying content + * this would give bogus results. */ + if (stream->ops->stat == NULL) { + return -1; + } + + return (stream->ops->stat)(stream, ssb TSRMLS_CC); +} + +PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC) +{ + size_t avail; + char *cr, *lf, *eol = NULL; + char *readptr; + + if (!buf) { + readptr = stream->readbuf + stream->readpos; + avail = stream->writepos - stream->readpos; + } else { + readptr = buf; + avail = buf_len; + } + + /* Look for EOL */ + if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) { + cr = memchr(readptr, '\r', avail); + lf = memchr(readptr, '\n', avail); + + if (cr && lf != cr + 1 && !(lf && lf < cr)) { + /* mac */ + stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; + stream->flags |= PHP_STREAM_FLAG_EOL_MAC; + eol = cr; + } else if ((cr && lf && cr == lf - 1) || (lf)) { + /* dos or unix endings */ + stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; + eol = lf; + } + } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) { + eol = memchr(readptr, '\r', avail); + } else { + /* unix (and dos) line endings */ + eol = memchr(readptr, '\n', avail); + } + + return eol; +} + +/* If buf == NULL, the buffer will be allocated automatically and will be of an + * appropriate length to hold the line, regardless of the line length, memory + * permitting */ +PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, + size_t *returned_len TSRMLS_DC) +{ + size_t avail = 0; + size_t current_buf_size = 0; + size_t total_copied = 0; + int grow_mode = 0; + char *bufstart = buf; + + if (buf == NULL) { + grow_mode = 1; + } else if (maxlen == 0) { + return NULL; + } + + /* + * If the underlying stream operations block when no new data is readable, + * we need to take extra precautions. + * + * If there is buffered data available, we check for a EOL. If it exists, + * we pass the data immediately back to the caller. This saves a call + * to the read implementation and will not block where blocking + * is not necessary at all. + * + * If the stream buffer contains more data than the caller requested, + * we can also avoid that costly step and simply return that data. + */ + + for (;;) { + avail = stream->writepos - stream->readpos; + + if (avail > 0) { + size_t cpysz = 0; + char *readptr; + char *eol; + int done = 0; + + readptr = stream->readbuf + stream->readpos; + eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC); + + if (eol) { + cpysz = eol - readptr + 1; + done = 1; + } else { + cpysz = avail; + } + + if (grow_mode) { + /* allow room for a NUL. If this realloc is really a realloc + * (ie: second time around), we get an extra byte. In most + * cases, with the default chunk size of 8K, we will only + * incur that overhead once. When people have lines longer + * than 8K, we waste 1 byte per additional 8K or so. + * That seems acceptable to me, to avoid making this code + * hard to follow */ + bufstart = erealloc(bufstart, current_buf_size + cpysz + 1); + current_buf_size += cpysz + 1; + buf = bufstart + total_copied; + } else { + if (cpysz >= maxlen - 1) { + cpysz = maxlen - 1; + done = 1; + } + } + + memcpy(buf, readptr, cpysz); + + stream->position += cpysz; + stream->readpos += cpysz; + buf += cpysz; + maxlen -= cpysz; + total_copied += cpysz; + + if (done) { + break; + } + } else if (stream->eof) { + break; + } else { + /* XXX: Should be fine to always read chunk_size */ + size_t toread; + + if (grow_mode) { + toread = stream->chunk_size; + } else { + toread = maxlen - 1; + if (toread > stream->chunk_size) { + toread = stream->chunk_size; + } + } + + php_stream_fill_read_buffer(stream, toread TSRMLS_CC); + + if (stream->writepos - stream->readpos == 0) { + break; + } + } + } + + if (total_copied == 0) { + if (grow_mode) { + assert(bufstart == NULL); + } + return NULL; + } + + buf[0] = '\0'; + if (returned_len) { + *returned_len = total_copied; + } + + return bufstart; +} + +#define STREAM_BUFFERED_AMOUNT(stream) \ + ((size_t)(((stream)->writepos) - (stream)->readpos)) + +static char *_php_stream_search_delim(php_stream *stream, + size_t maxlen, + size_t skiplen, + char *delim, /* non-empty! */ + size_t delim_len TSRMLS_DC) +{ + size_t seek_len; + + /* set the maximum number of bytes we're allowed to read from buffer */ + seek_len = MIN(STREAM_BUFFERED_AMOUNT(stream), maxlen); + if (seek_len <= skiplen) { + return NULL; + } + + if (delim_len == 1) { + return memchr(&stream->readbuf[stream->readpos + skiplen], + delim[0], seek_len - skiplen); + } else { + return php_memnstr((char*)&stream->readbuf[stream->readpos + skiplen], + delim, delim_len, + (char*)&stream->readbuf[stream->readpos + seek_len]); + } +} + +PHPAPI char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC) +{ + char *ret_buf, /* returned buffer */ + *found_delim = NULL; + size_t buffered_len, + tent_ret_len; /* tentative returned length */ + int has_delim = delim_len > 0; + + if (maxlen == 0) { + return NULL; + } + + if (has_delim) { + found_delim = _php_stream_search_delim( + stream, maxlen, 0, delim, delim_len TSRMLS_CC); + } + + buffered_len = STREAM_BUFFERED_AMOUNT(stream); + /* try to read up to maxlen length bytes while we don't find the delim */ + while (!found_delim && buffered_len < maxlen) { + size_t just_read, + to_read_now; + + to_read_now = MIN(maxlen - buffered_len, stream->chunk_size); + + php_stream_fill_read_buffer(stream, buffered_len + to_read_now TSRMLS_CC); + + just_read = STREAM_BUFFERED_AMOUNT(stream) - buffered_len; + + /* Assume the stream is temporarily or permanently out of data */ + if (just_read == 0) { + break; + } + + if (has_delim) { + /* search for delimiter, but skip buffered_len (the number of bytes + * buffered before this loop iteration), as they have already been + * searched for the delimiter. + * The left part of the delimiter may still remain in the buffer, + * so subtract up to from buffered_len, which is + * the ammount of data we skip on this search as an optimization + */ + found_delim = _php_stream_search_delim( + stream, maxlen, + buffered_len >= (delim_len - 1) + ? buffered_len - (delim_len - 1) + : 0, + delim, delim_len TSRMLS_CC); + if (found_delim) { + break; + } + } + buffered_len += just_read; + } + + if (has_delim && found_delim) { + tent_ret_len = found_delim - (char*)&stream->readbuf[stream->readpos]; + } else if (!has_delim && STREAM_BUFFERED_AMOUNT(stream) >= maxlen) { + tent_ret_len = maxlen; + } else { + /* return with error if the delimiter string (if any) was not found, we + * could not completely fill the read buffer with maxlen bytes and we + * don't know we've reached end of file. Added with non-blocking streams + * in mind, where this situation is frequent */ + if (STREAM_BUFFERED_AMOUNT(stream) < maxlen && !stream->eof) { + return NULL; + } else if (STREAM_BUFFERED_AMOUNT(stream) == 0 && stream->eof) { + /* refuse to return an empty string just because by accident + * we knew of EOF in a read that returned no data */ + return NULL; + } else { + tent_ret_len = MIN(STREAM_BUFFERED_AMOUNT(stream), maxlen); + } + } + + ret_buf = emalloc(tent_ret_len + 1); + /* php_stream_read will not call ops->read here because the necessary + * data is guaranteedly buffered */ + *returned_len = php_stream_read(stream, ret_buf, tent_ret_len); + + if (found_delim) { + stream->readpos += delim_len; + stream->position += delim_len; + } + ret_buf[*returned_len] = '\0'; + return ret_buf; +} + +/* Writes a buffer directly to a stream, using multiple of the chunk size */ +static size_t _php_stream_write_buffer(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + size_t didwrite = 0, towrite, justwrote; + + /* if we have a seekable stream we need to ensure that data is written at the + * current stream->position. This means invalidating the read buffer and then + * performing a low-level seek */ + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && stream->readpos != stream->writepos) { + stream->readpos = stream->writepos = 0; + + stream->ops->seek(stream, stream->position, SEEK_SET, &stream->position TSRMLS_CC); + } + + + while (count > 0) { + towrite = count; + if (towrite > stream->chunk_size) + towrite = stream->chunk_size; + + justwrote = stream->ops->write(stream, buf, towrite TSRMLS_CC); + + /* convert justwrote to an integer, since normally it is unsigned */ + if ((int)justwrote > 0) { + buf += justwrote; + count -= justwrote; + didwrite += justwrote; + + /* Only screw with the buffer if we can seek, otherwise we lose data + * buffered from fifos and sockets */ + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + stream->position += justwrote; + } + } else { + break; + } + } + return didwrite; + +} + +/* push some data through the write filter chain. + * buf may be NULL, if flags are set to indicate a flush. + * This may trigger a real write to the stream. + * Returns the number of bytes consumed from buf by the first filter in the chain. + * */ +static size_t _php_stream_write_filtered(php_stream *stream, const char *buf, size_t count, int flags TSRMLS_DC) +{ + size_t consumed = 0; + php_stream_bucket *bucket; + php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + php_stream_filter_status_t status = PSFS_ERR_FATAL; + php_stream_filter *filter; + + if (buf) { + bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0 TSRMLS_CC); + php_stream_bucket_append(&brig_in, bucket TSRMLS_CC); + } + + for (filter = stream->writefilters.head; filter; filter = filter->next) { + /* for our return value, we are interested in the number of bytes consumed from + * the first filter in the chain */ + status = filter->fops->filter(stream, filter, brig_inp, brig_outp, + filter == stream->writefilters.head ? &consumed : NULL, flags TSRMLS_CC); + + if (status != PSFS_PASS_ON) { + break; + } + /* brig_out becomes brig_in. + * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets + * to its own brigade */ + brig_swap = brig_inp; + brig_inp = brig_outp; + brig_outp = brig_swap; + memset(brig_outp, 0, sizeof(*brig_outp)); + } + + switch (status) { + case PSFS_PASS_ON: + /* filter chain generated some output; push it through to the + * underlying stream */ + while (brig_inp->head) { + bucket = brig_inp->head; + _php_stream_write_buffer(stream, bucket->buf, bucket->buflen TSRMLS_CC); + /* Potential error situation - eg: no space on device. Perhaps we should keep this brigade + * hanging around and try to write it later. + * At the moment, we just drop it on the floor + * */ + + php_stream_bucket_unlink(bucket TSRMLS_CC); + php_stream_bucket_delref(bucket TSRMLS_CC); + } + break; + case PSFS_FEED_ME: + /* need more data before we can push data through to the stream */ + break; + + case PSFS_ERR_FATAL: + /* some fatal error. Theoretically, the stream is borked, so all + * further writes should fail. */ + break; + } + + return consumed; +} + +PHPAPI int _php_stream_flush(php_stream *stream, int closing TSRMLS_DC) +{ + int ret = 0; + + if (stream->writefilters.head) { + _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC TSRMLS_CC); + } + + if (stream->ops->flush) { + ret = stream->ops->flush(stream TSRMLS_CC); + } + + return ret; +} + +PHPAPI size_t _php_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + if (buf == NULL || count == 0 || stream->ops->write == NULL) { + return 0; + } + + if (stream->writefilters.head) { + return _php_stream_write_filtered(stream, buf, count, PSFS_FLAG_NORMAL TSRMLS_CC); + } else { + return _php_stream_write_buffer(stream, buf, count TSRMLS_CC); + } +} + +PHPAPI size_t _php_stream_printf(php_stream *stream TSRMLS_DC, const char *fmt, ...) +{ + size_t count; + char *buf; + va_list ap; + + va_start(ap, fmt); + count = vspprintf(&buf, 0, fmt, ap); + va_end(ap); + + if (!buf) { + return 0; /* error condition */ + } + + count = php_stream_write(stream, buf, count); + efree(buf); + + return count; +} + +PHPAPI off_t _php_stream_tell(php_stream *stream TSRMLS_DC) +{ + return stream->position; +} + +PHPAPI int _php_stream_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC) +{ + if (stream->fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { + /* flush to commit data written to the fopencookie FILE* */ + fflush(stream->stdiocast); + } + + /* handle the case where we are in the buffer */ + if ((stream->flags & PHP_STREAM_FLAG_NO_BUFFER) == 0) { + switch(whence) { + case SEEK_CUR: + if (offset > 0 && offset <= stream->writepos - stream->readpos) { + stream->readpos += offset; /* if offset = ..., then readpos = writepos */ + stream->position += offset; + stream->eof = 0; + return 0; + } + break; + case SEEK_SET: + if (offset > stream->position && + offset <= stream->position + stream->writepos - stream->readpos) { + stream->readpos += offset - stream->position; + stream->position = offset; + stream->eof = 0; + return 0; + } + break; + } + } + + + if (stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) { + int ret; + + if (stream->writefilters.head) { + _php_stream_flush(stream, 0 TSRMLS_CC); + } + + switch(whence) { + case SEEK_CUR: + offset = stream->position + offset; + whence = SEEK_SET; + break; + } + ret = stream->ops->seek(stream, offset, whence, &stream->position TSRMLS_CC); + + if (((stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0) || ret == 0) { + if (ret == 0) { + stream->eof = 0; + } + + /* invalidate the buffer contents */ + stream->readpos = stream->writepos = 0; + + return ret; + } + /* else the stream has decided that it can't support seeking after all; + * fall through to attempt emulation */ + } + + /* emulate forward moving seeks with reads */ + if (whence == SEEK_CUR && offset >= 0) { + char tmp[1024]; + size_t didread; + while(offset > 0) { + if ((didread = php_stream_read(stream, tmp, MIN(offset, sizeof(tmp)))) == 0) { + return -1; + } + offset -= didread; + } + stream->eof = 0; + return 0; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream does not support seeking"); + + return -1; +} + +PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL; + + if (stream->ops->set_option) { + ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC); + } + + if (ret == PHP_STREAM_OPTION_RETURN_NOTIMPL) { + switch(option) { + case PHP_STREAM_OPTION_SET_CHUNK_SIZE: + ret = stream->chunk_size; + stream->chunk_size = value; + return ret; + + case PHP_STREAM_OPTION_READ_BUFFER: + /* try to match the buffer mode as best we can */ + if (value == PHP_STREAM_BUFFER_NONE) { + stream->flags |= PHP_STREAM_FLAG_NO_BUFFER; + } else if (stream->flags & PHP_STREAM_FLAG_NO_BUFFER) { + stream->flags ^= PHP_STREAM_FLAG_NO_BUFFER; + } + ret = PHP_STREAM_OPTION_RETURN_OK; + break; + + default: + ; + } + } + + return ret; +} + +PHPAPI int _php_stream_truncate_set_size(php_stream *stream, size_t newsize TSRMLS_DC) +{ + return php_stream_set_option(stream, PHP_STREAM_OPTION_TRUNCATE_API, PHP_STREAM_TRUNCATE_SET_SIZE, &newsize); +} + +PHPAPI size_t _php_stream_passthru(php_stream * stream STREAMS_DC TSRMLS_DC) +{ + size_t bcount = 0; + char buf[8192]; + int b; + + if (php_stream_mmap_possible(stream)) { + char *p; + size_t mapped; + + p = php_stream_mmap_range(stream, php_stream_tell(stream), PHP_STREAM_MMAP_ALL, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped); + + if (p) { + PHPWRITE(p, mapped); + + php_stream_mmap_unmap_ex(stream, mapped); + + return mapped; + } + } + + while ((b = php_stream_read(stream, buf, sizeof(buf))) > 0) { + PHPWRITE(buf, b); + bcount += b; + } + + return bcount; +} + + +PHPAPI size_t _php_stream_copy_to_mem(php_stream *src, char **buf, size_t maxlen, int persistent STREAMS_DC TSRMLS_DC) +{ + size_t ret = 0; + char *ptr; + size_t len = 0, max_len; + int step = CHUNK_SIZE; + int min_room = CHUNK_SIZE / 4; + php_stream_statbuf ssbuf; + + if (maxlen == 0) { + return 0; + } + + if (maxlen == PHP_STREAM_COPY_ALL) { + maxlen = 0; + } + + if (maxlen > 0) { + ptr = *buf = pemalloc_rel_orig(maxlen + 1, persistent); + while ((len < maxlen) && !php_stream_eof(src)) { + ret = php_stream_read(src, ptr, maxlen - len); + if (!ret) { + break; + } + len += ret; + ptr += ret; + } + if (len) { + *ptr = '\0'; + } else { + pefree(*buf, persistent); + *buf = NULL; + } + return len; + } + + /* avoid many reallocs by allocating a good sized chunk to begin with, if + * we can. Note that the stream may be filtered, in which case the stat + * result may be inaccurate, as the filter may inflate or deflate the + * number of bytes that we can read. In order to avoid an upsize followed + * by a downsize of the buffer, overestimate by the step size (which is + * 2K). */ + if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) { + max_len = ssbuf.sb.st_size + step; + } else { + max_len = step; + } + + ptr = *buf = pemalloc_rel_orig(max_len, persistent); + + while((ret = php_stream_read(src, ptr, max_len - len))) { + len += ret; + if (len + min_room >= max_len) { + *buf = perealloc_rel_orig(*buf, max_len + step, persistent); + max_len += step; + ptr = *buf + len; + } else { + ptr += ret; + } + } + if (len) { + *buf = perealloc_rel_orig(*buf, len + 1, persistent); + (*buf)[len] = '\0'; + } else { + pefree(*buf, persistent); + *buf = NULL; + } + return len; +} + +/* Returns SUCCESS/FAILURE and sets *len to the number of bytes moved */ +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; + size_t haveread = 0; + size_t didread; + size_t dummy; + php_stream_statbuf ssbuf; + + if (!len) { + len = &dummy; + } + + if (maxlen == 0) { + *len = 0; + return SUCCESS; + } + + if (maxlen == PHP_STREAM_COPY_ALL) { + maxlen = 0; + } + + if (php_stream_stat(src, &ssbuf) == 0) { + if (ssbuf.sb.st_size == 0 +#ifdef S_ISREG + && S_ISREG(ssbuf.sb.st_mode) +#endif + ) { + *len = 0; + return SUCCESS; + } + } + + if (php_stream_mmap_possible(src)) { + char *p; + size_t mapped; + + p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped); + + if (p) { + mapped = php_stream_write(dest, p, mapped); + + php_stream_mmap_unmap_ex(src, mapped); + + *len = mapped; + + /* we've got at least 1 byte to read. + * less than 1 is an error */ + + if (mapped > 0) { + return SUCCESS; + } + return FAILURE; + } + } + + while(1) { + readchunk = sizeof(buf); + + if (maxlen && (maxlen - haveread) < readchunk) { + readchunk = maxlen - haveread; + } + + didread = php_stream_read(src, buf, readchunk); + + if (didread) { + /* extra paranoid */ + size_t didwrite, towrite; + char *writeptr; + + towrite = didread; + writeptr = buf; + haveread += didread; + + while(towrite) { + didwrite = php_stream_write(dest, writeptr, towrite); + if (didwrite == 0) { + *len = haveread - (didread - towrite); + return FAILURE; + } + + towrite -= didwrite; + writeptr += didwrite; + } + } else { + break; + } + + if (maxlen - haveread == 0) { + break; + } + } + + *len = haveread; + + /* we've got at least 1 byte to read. + * less than 1 is an error */ + + if (haveread > 0 || src->eof) { + return SUCCESS; + } + return FAILURE; +} + +/* Returns the number of bytes moved. + * Returns 1 when source len is 0. + * Deprecated in favor of php_stream_copy_to_stream_ex() */ +ZEND_ATTRIBUTE_DEPRECATED +PHPAPI size_t _php_stream_copy_to_stream(php_stream *src, php_stream *dest, size_t maxlen STREAMS_DC TSRMLS_DC) +{ + size_t len; + int ret = _php_stream_copy_to_stream_ex(src, dest, maxlen, &len STREAMS_REL_CC TSRMLS_CC); + if (ret == SUCCESS && len == 0 && maxlen != 0) { + return 1; + } + return len; +} +/* }}} */ + +/* {{{ wrapper init and registration */ + +static void stream_resource_regular_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_stream *stream = (php_stream*)rsrc->ptr; + /* set the return value for pclose */ + FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); +} + +static void stream_resource_persistent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + php_stream *stream = (php_stream*)rsrc->ptr; + FG(pclose_ret) = php_stream_free(stream, PHP_STREAM_FREE_CLOSE | PHP_STREAM_FREE_RSRC_DTOR); +} + +void php_shutdown_stream_hashes(TSRMLS_D) +{ + if (FG(stream_wrappers)) { + zend_hash_destroy(FG(stream_wrappers)); + efree(FG(stream_wrappers)); + FG(stream_wrappers) = NULL; + } + + if (FG(stream_filters)) { + zend_hash_destroy(FG(stream_filters)); + efree(FG(stream_filters)); + FG(stream_filters) = NULL; + } + + if (FG(wrapper_errors)) { + zend_hash_destroy(FG(wrapper_errors)); + efree(FG(wrapper_errors)); + FG(wrapper_errors) = NULL; + } +} + +int php_init_stream_wrappers(int module_number TSRMLS_DC) +{ + le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number); + le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number); + + /* Filters are cleaned up by the streams they're attached to */ + le_stream_filter = zend_register_list_destructors_ex(NULL, NULL, "stream filter", module_number); + + return ( + zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS + && + zend_hash_init(php_get_stream_filters_hash_global(), 0, NULL, NULL, 1) == SUCCESS + && + zend_hash_init(php_stream_xport_get_hash(), 0, NULL, NULL, 1) == SUCCESS + && + php_stream_xport_register("tcp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS + && + php_stream_xport_register("udp", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS +#if defined(AF_UNIX) && !(defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)) + && + php_stream_xport_register("unix", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS + && + php_stream_xport_register("udg", php_stream_generic_socket_factory TSRMLS_CC) == SUCCESS +#endif + ) ? SUCCESS : FAILURE; +} + +int php_shutdown_stream_wrappers(int module_number TSRMLS_DC) +{ + zend_hash_destroy(&url_stream_wrappers_hash); + zend_hash_destroy(php_get_stream_filters_hash_global()); + zend_hash_destroy(php_stream_xport_get_hash()); + return SUCCESS; +} + +/* Validate protocol scheme names during registration + * Must conform to /^[a-zA-Z0-9+.-]+$/ + */ +static inline int php_stream_wrapper_scheme_validate(char *protocol, int protocol_len) +{ + int i; + + for(i = 0; i < protocol_len; i++) { + if (!isalnum((int)protocol[i]) && + protocol[i] != '+' && + protocol[i] != '-' && + protocol[i] != '.') { + return FAILURE; + } + } + + return SUCCESS; +} + +/* API for registering GLOBAL wrappers */ +PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC) +{ + int protocol_len = strlen(protocol); + + if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) { + return FAILURE; + } + + return zend_hash_add(&url_stream_wrappers_hash, protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL); +} + +PHPAPI int php_unregister_url_stream_wrapper(char *protocol TSRMLS_DC) +{ + return zend_hash_del(&url_stream_wrappers_hash, protocol, strlen(protocol) + 1); +} + +static void clone_wrapper_hash(TSRMLS_D) +{ + php_stream_wrapper *tmp; + + ALLOC_HASHTABLE(FG(stream_wrappers)); + zend_hash_init(FG(stream_wrappers), zend_hash_num_elements(&url_stream_wrappers_hash), NULL, NULL, 1); + zend_hash_copy(FG(stream_wrappers), &url_stream_wrappers_hash, NULL, &tmp, sizeof(tmp)); +} + +/* API for registering VOLATILE wrappers */ +PHPAPI int php_register_url_stream_wrapper_volatile(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC) +{ + int protocol_len = strlen(protocol); + + if (php_stream_wrapper_scheme_validate(protocol, protocol_len) == FAILURE) { + return FAILURE; + } + + if (!FG(stream_wrappers)) { + clone_wrapper_hash(TSRMLS_C); + } + + return zend_hash_add(FG(stream_wrappers), protocol, protocol_len + 1, &wrapper, sizeof(wrapper), NULL); +} + +PHPAPI int php_unregister_url_stream_wrapper_volatile(char *protocol TSRMLS_DC) +{ + if (!FG(stream_wrappers)) { + clone_wrapper_hash(TSRMLS_C); + } + + return zend_hash_del(FG(stream_wrappers), protocol, strlen(protocol) + 1); +} +/* }}} */ + +/* {{{ php_stream_locate_url_wrapper */ +PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC) +{ + HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); + php_stream_wrapper **wrapperpp = NULL; + const char *p, *protocol = NULL; + int n = 0; + + if (path_for_open) { + *path_for_open = (char*)path; + } + + if (options & IGNORE_URL) { + return (options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper; + } + + for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { + n++; + } + + if ((*p == ':') && (n > 1) && (!strncmp("//", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) { + protocol = path; + } else if (n == 5 && strncasecmp(path, "zlib:", 5) == 0) { + /* BC with older php scripts and zlib wrapper */ + protocol = "compress.zlib"; + n = 13; + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Use of \"zlib:\" wrapper is deprecated; please use \"compress.zlib://\" instead"); + } + + if (protocol) { + char *tmp = estrndup(protocol, n); + if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) { + php_strtolower(tmp, n); + if (FAILURE == zend_hash_find(wrapper_hash, (char*)tmp, n + 1, (void**)&wrapperpp)) { + char wrapper_name[32]; + + if (n >= sizeof(wrapper_name)) { + n = sizeof(wrapper_name) - 1; + } + PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find the wrapper \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); + + wrapperpp = NULL; + protocol = NULL; + } + } + efree(tmp); + } + /* TODO: curl based streams probably support file:// properly */ + if (!protocol || !strncasecmp(protocol, "file", n)) { + /* fall back on regular file access */ + php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper; + + if (protocol) { + int localhost = 0; + + if (!strncasecmp(path, "file://localhost/", 17)) { + localhost = 1; + } + +#ifdef PHP_WIN32 + if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':') { +#else + if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') { +#endif + if (options & REPORT_ERRORS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "remote host file access not supported, %s", path); + } + return NULL; + } + + if (path_for_open) { + /* skip past protocol and :/, but handle windows correctly */ + *path_for_open = (char*)path + n + 1; + if (localhost == 1) { + (*path_for_open) += 11; + } + while (*(++*path_for_open)=='/'); +#ifdef PHP_WIN32 + if (*(*path_for_open + 1) != ':') +#endif + (*path_for_open)--; + } + } + + if (options & STREAM_LOCATE_WRAPPERS_ONLY) { + return NULL; + } + + if (FG(stream_wrappers)) { + /* The file:// wrapper may have been disabled/overridden */ + + if (wrapperpp) { + /* It was found so go ahead and provide it */ + return *wrapperpp; + } + + /* Check again, the original check might have not known the protocol name */ + if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == SUCCESS) { + return *wrapperpp; + } + + if (options & REPORT_ERRORS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "file:// wrapper is disabled in the server configuration"); + } + return NULL; + } + + return plain_files_wrapper; + } + + if (wrapperpp && (*wrapperpp)->is_url && + (options & STREAM_DISABLE_URL_PROTECTION) == 0 && + (!PG(allow_url_fopen) || + (((options & STREAM_OPEN_FOR_INCLUDE) || + PG(in_user_include)) && !PG(allow_url_include)))) { + if (options & REPORT_ERRORS) { + /* protocol[n] probably isn't '\0' */ + char *protocol_dup = estrndup(protocol, n); + if (!PG(allow_url_fopen)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_fopen=0", protocol_dup); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration by allow_url_include=0", protocol_dup); + } + efree(protocol_dup); + } + return NULL; + } + + return *wrapperpp; +} +/* }}} */ + +/* {{{ _php_stream_mkdir + */ +PHPAPI int _php_stream_mkdir(char *path, int mode, int options, php_stream_context *context TSRMLS_DC) +{ + php_stream_wrapper *wrapper = NULL; + + wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC); + if (!wrapper || !wrapper->wops || !wrapper->wops->stream_mkdir) { + return 0; + } + + return wrapper->wops->stream_mkdir(wrapper, path, mode, options, context TSRMLS_CC); +} +/* }}} */ + +/* {{{ _php_stream_rmdir + */ +PHPAPI int _php_stream_rmdir(char *path, int options, php_stream_context *context TSRMLS_DC) +{ + php_stream_wrapper *wrapper = NULL; + + wrapper = php_stream_locate_url_wrapper(path, NULL, 0 TSRMLS_CC); + if (!wrapper || !wrapper->wops || !wrapper->wops->stream_rmdir) { + return 0; + } + + return wrapper->wops->stream_rmdir(wrapper, path, options, context TSRMLS_CC); +} +/* }}} */ + +/* {{{ _php_stream_stat_path */ +PHPAPI int _php_stream_stat_path(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) +{ + php_stream_wrapper *wrapper = NULL; + char *path_to_open = path; + int ret; + + /* Try to hit the cache first */ + if (flags & PHP_STREAM_URL_STAT_LINK) { + if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) { + memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf)); + return 0; + } + } else { + if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) { + memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf)); + return 0; + } + } + + 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) { + /* Drop into cache */ + if (flags & PHP_STREAM_URL_STAT_LINK) { + if (BG(CurrentLStatFile)) { + efree(BG(CurrentLStatFile)); + } + BG(CurrentLStatFile) = estrdup(path); + memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf)); + } else { + if (BG(CurrentStatFile)) { + efree(BG(CurrentStatFile)); + } + BG(CurrentStatFile) = estrdup(path); + memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf)); + } + } + return ret; + } + return -1; +} +/* }}} */ + +/* {{{ php_stream_opendir */ +PHPAPI php_stream *_php_stream_opendir(char *path, int options, + php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_stream_wrapper *wrapper = NULL; + char *path_to_open; + + if (!path || !*path) { + return NULL; + } + + path_to_open = path; + + wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); + + if (wrapper && wrapper->wops->dir_opener) { + stream = wrapper->wops->dir_opener(wrapper, + path_to_open, "r", options ^ REPORT_ERRORS, NULL, + context STREAMS_REL_CC TSRMLS_CC); + + if (stream) { + stream->wrapper = wrapper; + stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; + } + } else if (wrapper) { + php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, "not implemented"); + } + if (stream == NULL && (options & REPORT_ERRORS)) { + php_stream_display_wrapper_errors(wrapper, path, "failed to open dir" TSRMLS_CC); + } + php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC); + + return stream; +} +/* }}} */ + +/* {{{ _php_stream_readdir */ +PHPAPI php_stream_dirent *_php_stream_readdir(php_stream *dirstream, php_stream_dirent *ent TSRMLS_DC) +{ + + if (sizeof(php_stream_dirent) == php_stream_read(dirstream, (char*)ent, sizeof(php_stream_dirent))) { + return ent; + } + + return NULL; +} +/* }}} */ + +/* {{{ php_stream_open_wrapper_ex */ +PHPAPI php_stream *_php_stream_open_wrapper_ex(char *path, char *mode, int options, + char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_stream_wrapper *wrapper = NULL; + char *path_to_open; + int persistent = options & STREAM_OPEN_PERSISTENT; + char *resolved_path = NULL; + char *copy_of_path = NULL; + + if (opened_path) { + *opened_path = NULL; + } + + if (!path || !*path) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename cannot be empty"); + return NULL; + } + + if (options & USE_PATH) { + resolved_path = zend_resolve_path(path, strlen(path) TSRMLS_CC); + if (resolved_path) { + path = resolved_path; + /* we've found this file, don't re-check include_path or run realpath */ + options |= STREAM_ASSUME_REALPATH; + options &= ~USE_PATH; + } + } + + path_to_open = path; + + wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options TSRMLS_CC); + if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function may only be used against URLs"); + if (resolved_path) { + efree(resolved_path); + } + return NULL; + } + + if (wrapper) { + if (!wrapper->wops->stream_opener) { + php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, + "wrapper does not support stream open"); + } else { + stream = wrapper->wops->stream_opener(wrapper, + path_to_open, mode, options ^ REPORT_ERRORS, + opened_path, context STREAMS_REL_CC TSRMLS_CC); + } + + /* if the caller asked for a persistent stream but the wrapper did not + * return one, force an error here */ + if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) { + php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS TSRMLS_CC, + "wrapper does not support persistent streams"); + php_stream_close(stream); + stream = NULL; + } + + if (stream) { + stream->wrapper = wrapper; + } + } + + if (stream) { + if (opened_path && !*opened_path && resolved_path) { + *opened_path = resolved_path; + resolved_path = NULL; + } + if (stream->orig_path) { + pefree(stream->orig_path, persistent); + } + copy_of_path = pestrdup(path, persistent); + stream->orig_path = copy_of_path; +#if ZEND_DEBUG + stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; + stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; +#endif + } + + if (stream != NULL && (options & STREAM_MUST_SEEK)) { + php_stream *newstream; + + switch(php_stream_make_seekable_rel(stream, &newstream, + (options & STREAM_WILL_CAST) + ? PHP_STREAM_PREFER_STDIO : PHP_STREAM_NO_PREFERENCE)) { + case PHP_STREAM_UNCHANGED: + if (resolved_path) { + efree(resolved_path); + } + return stream; + case PHP_STREAM_RELEASED: + if (newstream->orig_path) { + pefree(newstream->orig_path, persistent); + } + newstream->orig_path = pestrdup(path, persistent); + if (resolved_path) { + efree(resolved_path); + } + return newstream; + default: + php_stream_close(stream); + stream = NULL; + if (options & REPORT_ERRORS) { + char *tmp = estrdup(path); + php_strip_url_passwd(tmp); + php_error_docref1(NULL TSRMLS_CC, tmp, E_WARNING, "could not make seekable - %s", + tmp); + efree(tmp); + + options ^= REPORT_ERRORS; + } + } + } + + if (stream && stream->ops->seek && (stream->flags & PHP_STREAM_FLAG_NO_SEEK) == 0 && strchr(mode, 'a') && stream->position == 0) { + off_t newpos = 0; + + /* if opened for append, we need to revise our idea of the initial file position */ + if (0 == stream->ops->seek(stream, 0, SEEK_CUR, &newpos TSRMLS_CC)) { + stream->position = newpos; + } + } + + if (stream == NULL && (options & REPORT_ERRORS)) { + php_stream_display_wrapper_errors(wrapper, path, "failed to open stream" TSRMLS_CC); + if (opened_path && *opened_path) { + efree(*opened_path); + *opened_path = NULL; + } + } + php_stream_tidy_wrapper_error_log(wrapper TSRMLS_CC); +#if ZEND_DEBUG + if (stream == NULL && copy_of_path != NULL) { + pefree(copy_of_path, persistent); + } +#endif + if (resolved_path) { + efree(resolved_path); + } + return stream; +} +/* }}} */ + +/* {{{ context API */ +PHPAPI php_stream_context *php_stream_context_set(php_stream *stream, php_stream_context *context) +{ + php_stream_context *oldcontext = stream->context; + TSRMLS_FETCH(); + + stream->context = context; + + if (context) { + zend_list_addref(context->rsrc_id); + } + if (oldcontext) { + zend_list_delete(oldcontext->rsrc_id); + } + + return oldcontext; +} + +PHPAPI void php_stream_notification_notify(php_stream_context *context, int notifycode, int severity, + char *xmsg, int xcode, size_t bytes_sofar, size_t bytes_max, void * ptr TSRMLS_DC) +{ + if (context && context->notifier) + context->notifier->func(context, notifycode, severity, xmsg, xcode, bytes_sofar, bytes_max, ptr TSRMLS_CC); +} + +PHPAPI void php_stream_context_free(php_stream_context *context) +{ + if (context->options) { + zval_ptr_dtor(&context->options); + context->options = NULL; + } + if (context->notifier) { + php_stream_notification_free(context->notifier); + context->notifier = NULL; + } + if (context->links) { + zval_ptr_dtor(&context->links); + context->links = NULL; + } + efree(context); +} + +PHPAPI php_stream_context *php_stream_context_alloc(TSRMLS_D) +{ + php_stream_context *context; + + context = ecalloc(1, sizeof(php_stream_context)); + context->notifier = NULL; + MAKE_STD_ZVAL(context->options); + array_init(context->options); + + context->rsrc_id = ZEND_REGISTER_RESOURCE(NULL, context, php_le_stream_context(TSRMLS_C)); + return context; +} + +PHPAPI php_stream_notifier *php_stream_notification_alloc(void) +{ + return ecalloc(1, sizeof(php_stream_notifier)); +} + +PHPAPI void php_stream_notification_free(php_stream_notifier *notifier) +{ + if (notifier->dtor) { + notifier->dtor(notifier); + } + efree(notifier); +} + +PHPAPI int php_stream_context_get_option(php_stream_context *context, + const char *wrappername, const char *optionname, zval ***optionvalue) +{ + zval **wrapperhash; + + if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) { + return FAILURE; + } + return zend_hash_find(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)optionvalue); +} + +PHPAPI int php_stream_context_set_option(php_stream_context *context, + const char *wrappername, const char *optionname, zval *optionvalue) +{ + zval **wrapperhash; + zval *category, *copied_val; + + ALLOC_INIT_ZVAL(copied_val); + *copied_val = *optionvalue; + zval_copy_ctor(copied_val); + INIT_PZVAL(copied_val); + + if (FAILURE == zend_hash_find(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&wrapperhash)) { + MAKE_STD_ZVAL(category); + array_init(category); + if (FAILURE == zend_hash_update(Z_ARRVAL_P(context->options), (char*)wrappername, strlen(wrappername)+1, (void**)&category, sizeof(zval *), NULL)) { + return FAILURE; + } + + wrapperhash = &category; + } + return zend_hash_update(Z_ARRVAL_PP(wrapperhash), (char*)optionname, strlen(optionname)+1, (void**)&copied_val, sizeof(zval *), NULL); +} + +PHPAPI int php_stream_context_get_link(php_stream_context *context, + const char *hostent, php_stream **stream) +{ + php_stream **pstream; + + if (!stream || !hostent || !context || !(context->links)) { + return FAILURE; + } + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&pstream)) { + *stream = *pstream; + return SUCCESS; + } + return FAILURE; +} + +PHPAPI int php_stream_context_set_link(php_stream_context *context, + const char *hostent, php_stream *stream) +{ + if (!context) { + return FAILURE; + } + if (!context->links) { + ALLOC_INIT_ZVAL(context->links); + array_init(context->links); + } + if (!stream) { + /* Delete any entry for */ + return zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1); + } + return zend_hash_update(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1, (void**)&stream, sizeof(php_stream *), NULL); +} + +PHPAPI int php_stream_context_del_link(php_stream_context *context, + php_stream *stream) +{ + php_stream **pstream; + char *hostent; + int ret = SUCCESS; + + if (!context || !context->links || !stream) { + return FAILURE; + } + + for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(context->links)); + SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(context->links), (void**)&pstream); + zend_hash_move_forward(Z_ARRVAL_P(context->links))) { + if (*pstream == stream) { + if (SUCCESS == zend_hash_get_current_key(Z_ARRVAL_P(context->links), &hostent, NULL, 0)) { + if (FAILURE == zend_hash_del(Z_ARRVAL_P(context->links), (char*)hostent, strlen(hostent)+1)) { + ret = FAILURE; + } + } else { + ret = FAILURE; + } + } + } + + return ret; +} +/* }}} */ + +/* {{{ php_stream_dirent_alphasort + */ +PHPAPI int php_stream_dirent_alphasort(const char **a, const char **b) +{ + return strcoll(*a, *b); +} +/* }}} */ + +/* {{{ php_stream_dirent_alphasortr + */ +PHPAPI int php_stream_dirent_alphasortr(const char **a, const char **b) +{ + return strcoll(*b, *a); +} +/* }}} */ + +/* {{{ php_stream_scandir + */ +PHPAPI int _php_stream_scandir(char *dirname, char **namelist[], int flags, php_stream_context *context, + int (*compare) (const char **a, const char **b) TSRMLS_DC) +{ + php_stream *stream; + php_stream_dirent sdp; + char **vector = NULL; + unsigned int vector_size = 0; + unsigned int nfiles = 0; + + if (!namelist) { + return FAILURE; + } + + stream = php_stream_opendir(dirname, REPORT_ERRORS, context); + if (!stream) { + return FAILURE; + } + + while (php_stream_readdir(stream, &sdp)) { + if (nfiles == vector_size) { + if (vector_size == 0) { + vector_size = 10; + } else { + if(vector_size*2 < vector_size) { + /* overflow */ + efree(vector); + return FAILURE; + } + vector_size *= 2; + } + vector = (char **) safe_erealloc(vector, vector_size, sizeof(char *), 0); + } + + vector[nfiles] = estrdup(sdp.d_name); + + nfiles++; + if(vector_size < 10 || nfiles == 0) { + /* overflow */ + efree(vector); + return FAILURE; + } + } + php_stream_closedir(stream); + + *namelist = vector; + + if (compare) { + qsort(*namelist, nfiles, sizeof(char *), (int(*)(const void *, const void *))compare); + } + return nfiles; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/transports.c b/main/streams/transports.c new file mode 100644 index 0000000..c24bf97 --- /dev/null +++ b/main/streams/transports.c @@ -0,0 +1,532 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "php_streams_int.h" +#include "ext/standard/file.h" + +static HashTable xport_hash; + +PHPAPI HashTable *php_stream_xport_get_hash(void) +{ + return &xport_hash; +} + +PHPAPI int php_stream_xport_register(char *protocol, php_stream_transport_factory factory TSRMLS_DC) +{ + return zend_hash_update(&xport_hash, protocol, strlen(protocol) + 1, &factory, sizeof(factory), NULL); +} + +PHPAPI int php_stream_xport_unregister(char *protocol TSRMLS_DC) +{ + return zend_hash_del(&xport_hash, protocol, strlen(protocol) + 1); +} + +#define ERR_REPORT(out_err, fmt, arg) \ + if (out_err) { spprintf(out_err, 0, fmt, arg); } \ + else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, arg); } + +#define ERR_RETURN(out_err, local_err, fmt) \ + if (out_err) { *out_err = local_err; } \ + else { php_error_docref(NULL TSRMLS_CC, E_WARNING, fmt, local_err ? local_err : "Unspecified error"); \ + if (local_err) { efree(local_err); local_err = NULL; } \ + } + +PHPAPI php_stream *_php_stream_xport_create(const char *name, long namelen, int options, + int flags, const char *persistent_id, + struct timeval *timeout, + php_stream_context *context, + char **error_string, + int *error_code + STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_stream_transport_factory *factory = NULL; + const char *p, *protocol = NULL; + int n = 0, failed = 0; + char *error_text = NULL; + struct timeval default_timeout = { 0, 0 }; + + default_timeout.tv_sec = FG(default_socket_timeout); + + if (timeout == NULL) { + timeout = &default_timeout; + } + + /* check for a cached persistent socket */ + if (persistent_id) { + switch(php_stream_from_persistent_id(persistent_id, &stream TSRMLS_CC)) { + case PHP_STREAM_PERSISTENT_SUCCESS: + /* use a 0 second timeout when checking if the socket + * has already died */ + if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_CHECK_LIVENESS, 0, NULL)) { + return stream; + } + /* dead - kill it */ + php_stream_pclose(stream); + stream = NULL; + + /* fall through */ + + case PHP_STREAM_PERSISTENT_FAILURE: + default: + /* failed; get a new one */ + ; + } + } + + for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { + n++; + } + + if ((*p == ':') && (n > 1) && !strncmp("://", p, 3)) { + protocol = name; + name = p + 3; + namelen -= n + 3; + } else { + protocol = "tcp"; + n = 3; + } + + if (protocol) { + char *tmp = estrndup(protocol, n); + if (FAILURE == zend_hash_find(&xport_hash, (char*)tmp, n + 1, (void**)&factory)) { + char wrapper_name[32]; + + if (n >= sizeof(wrapper_name)) + n = sizeof(wrapper_name) - 1; + PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); + + ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", + wrapper_name); + + efree(tmp); + return NULL; + } + efree(tmp); + } + + if (factory == NULL) { + /* should never happen */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find a factory !?"); + return NULL; + } + + stream = (*factory)(protocol, n, + (char*)name, namelen, persistent_id, options, flags, timeout, + context STREAMS_REL_CC TSRMLS_CC); + + if (stream) { + php_stream_context_set(stream, context); + + if ((flags & STREAM_XPORT_SERVER) == 0) { + /* client */ + + if (flags & (STREAM_XPORT_CONNECT|STREAM_XPORT_CONNECT_ASYNC)) { + if (-1 == php_stream_xport_connect(stream, name, namelen, + flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0, + timeout, &error_text, error_code TSRMLS_CC)) { + + ERR_RETURN(error_string, error_text, "connect() failed: %s"); + + failed = 1; + } + } + + } else { + /* server */ + if (flags & STREAM_XPORT_BIND) { + if (0 != php_stream_xport_bind(stream, name, namelen, &error_text TSRMLS_CC)) { + ERR_RETURN(error_string, error_text, "bind() failed: %s"); + failed = 1; + } else if (flags & STREAM_XPORT_LISTEN) { + zval **zbacklog = NULL; + int backlog = 32; + + if (stream->context && php_stream_context_get_option(stream->context, "socket", "backlog", &zbacklog) == SUCCESS) { + zval *ztmp = *zbacklog; + + convert_to_long_ex(&ztmp); + backlog = Z_LVAL_P(ztmp); + if (ztmp != *zbacklog) { + zval_ptr_dtor(&ztmp); + } + } + + if (0 != php_stream_xport_listen(stream, backlog, &error_text TSRMLS_CC)) { + ERR_RETURN(error_string, error_text, "listen() failed: %s"); + failed = 1; + } + } + } + } + } + + if (failed) { + /* failure means that they don't get a stream to play with */ + if (persistent_id) { + php_stream_pclose(stream); + } else { + php_stream_close(stream); + } + stream = NULL; + } + + return stream; +} + +/* Bind the stream to a local address */ +PHPAPI int php_stream_xport_bind(php_stream *stream, + const char *name, long namelen, + char **error_text + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = STREAM_XPORT_OP_BIND; + param.inputs.name = (char*)name; + param.inputs.namelen = namelen; + param.want_errortext = error_text ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (error_text) { + *error_text = param.outputs.error_text; + } + + return param.outputs.returncode; + } + + return ret; +} + +/* Connect to a remote address */ +PHPAPI int php_stream_xport_connect(php_stream *stream, + const char *name, long namelen, + int asynchronous, + struct timeval *timeout, + char **error_text, + int *error_code + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = asynchronous ? STREAM_XPORT_OP_CONNECT_ASYNC: STREAM_XPORT_OP_CONNECT; + param.inputs.name = (char*)name; + param.inputs.namelen = namelen; + param.inputs.timeout = timeout; + + param.want_errortext = error_text ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (error_text) { + *error_text = param.outputs.error_text; + } + if (error_code) { + *error_code = param.outputs.error_code; + } + return param.outputs.returncode; + } + + return ret; + +} + +/* Prepare to listen */ +PHPAPI int php_stream_xport_listen(php_stream *stream, int backlog, char **error_text TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = STREAM_XPORT_OP_LISTEN; + param.inputs.backlog = backlog; + param.want_errortext = error_text ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (error_text) { + *error_text = param.outputs.error_text; + } + + return param.outputs.returncode; + } + + return ret; +} + +/* Get the next client and their address (as a string) */ +PHPAPI int php_stream_xport_accept(php_stream *stream, php_stream **client, + char **textaddr, int *textaddrlen, + void **addr, socklen_t *addrlen, + struct timeval *timeout, + char **error_text + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + + param.op = STREAM_XPORT_OP_ACCEPT; + param.inputs.timeout = timeout; + param.want_addr = addr ? 1 : 0; + param.want_textaddr = textaddr ? 1 : 0; + param.want_errortext = error_text ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + *client = param.outputs.client; + if (addr) { + *addr = param.outputs.addr; + *addrlen = param.outputs.addrlen; + } + if (textaddr) { + *textaddr = param.outputs.textaddr; + *textaddrlen = param.outputs.textaddrlen; + } + if (error_text) { + *error_text = param.outputs.error_text; + } + + return param.outputs.returncode; + } + return ret; +} + +PHPAPI int php_stream_xport_get_name(php_stream *stream, int want_peer, + char **textaddr, int *textaddrlen, + void **addr, socklen_t *addrlen + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + + param.op = want_peer ? STREAM_XPORT_OP_GET_PEER_NAME : STREAM_XPORT_OP_GET_NAME; + param.want_addr = addr ? 1 : 0; + param.want_textaddr = textaddr ? 1 : 0; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (addr) { + *addr = param.outputs.addr; + *addrlen = param.outputs.addrlen; + } + if (textaddr) { + *textaddr = param.outputs.textaddr; + *textaddrlen = param.outputs.textaddrlen; + } + + return param.outputs.returncode; + } + return ret; +} + +PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream TSRMLS_DC) +{ + php_stream_xport_crypto_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = STREAM_XPORT_CRYPTO_OP_SETUP; + param.inputs.method = crypto_method; + param.inputs.session = session_stream; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + + php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto"); + + return ret; +} + +PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate TSRMLS_DC) +{ + php_stream_xport_crypto_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = STREAM_XPORT_CRYPTO_OP_ENABLE; + param.inputs.activate = activate; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + + php_error_docref("streams.crypto" TSRMLS_CC, E_WARNING, "this stream does not support SSL/crypto"); + + return ret; +} + +/* Similar to recv() system call; read data from the stream, optionally + * peeking, optionally retrieving OOB data */ +PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen, + long flags, void **addr, socklen_t *addrlen, char **textaddr, int *textaddrlen + TSRMLS_DC) +{ + php_stream_xport_param param; + int ret = 0; + int recvd_len = 0; +#if 0 + int oob; + + if (flags == 0 && addr == NULL) { + return php_stream_read(stream, buf, buflen); + } + + if (stream->readfilters.head) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot peek or fetch OOB data from a filtered stream"); + return -1; + } + + oob = (flags & STREAM_OOB) == STREAM_OOB; + + if (!oob && addr == NULL) { + /* must be peeking at regular data; copy content from the buffer + * first, then adjust the pointer/len before handing off to the + * stream */ + recvd_len = stream->writepos - stream->readpos; + if (recvd_len > buflen) { + recvd_len = buflen; + } + if (recvd_len) { + memcpy(buf, stream->readbuf, recvd_len); + buf += recvd_len; + buflen -= recvd_len; + } + /* if we filled their buffer, return */ + if (buflen == 0) { + return recvd_len; + } + } +#endif + + /* otherwise, we are going to bypass the buffer */ + + memset(¶m, 0, sizeof(param)); + + param.op = STREAM_XPORT_OP_RECV; + param.want_addr = addr ? 1 : 0; + param.want_textaddr = textaddr ? 1 : 0; + param.inputs.buf = buf; + param.inputs.buflen = buflen; + param.inputs.flags = flags; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + if (addr) { + *addr = param.outputs.addr; + *addrlen = param.outputs.addrlen; + } + if (textaddr) { + *textaddr = param.outputs.textaddr; + *textaddrlen = param.outputs.textaddrlen; + } + return recvd_len + param.outputs.returncode; + } + return recvd_len ? recvd_len : -1; +} + +/* Similar to send() system call; send data to the stream, optionally + * sending it as OOB data */ +PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t buflen, + long flags, void *addr, socklen_t addrlen TSRMLS_DC) +{ + php_stream_xport_param param; + int ret = 0; + int oob; + +#if 0 + if (flags == 0 && addr == NULL) { + return php_stream_write(stream, buf, buflen); + } +#endif + + oob = (flags & STREAM_OOB) == STREAM_OOB; + + if ((oob || addr) && stream->writefilters.head) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot write OOB data, or data to a targeted address on a filtered stream"); + return -1; + } + + memset(¶m, 0, sizeof(param)); + + param.op = STREAM_XPORT_OP_SEND; + param.want_addr = addr ? 1 : 0; + param.inputs.buf = (char*)buf; + param.inputs.buflen = buflen; + param.inputs.flags = flags; + param.inputs.addr = addr; + param.inputs.addrlen = addrlen; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + return -1; +} + +/* Similar to shutdown() system call; shut down part of a full-duplex + * connection */ +PHPAPI int php_stream_xport_shutdown(php_stream *stream, stream_shutdown_t how TSRMLS_DC) +{ + php_stream_xport_param param; + int ret = 0; + + memset(¶m, 0, sizeof(param)); + + param.op = STREAM_XPORT_OP_SHUTDOWN; + param.how = how; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_XPORT_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + return -1; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/streams/userspace.c b/main/streams/userspace.c new file mode 100644 index 0000000..69edbaa --- /dev/null +++ b/main/streams/userspace.c @@ -0,0 +1,1676 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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. | + +----------------------------------------------------------------------+ + | Authors: Wez Furlong | + | Sara Golemon | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "php_globals.h" +#include "ext/standard/file.h" +#include "ext/standard/flock_compat.h" +#ifdef HAVE_SYS_FILE_H +#include +#endif +#include + +#if HAVE_UTIME +# ifdef PHP_WIN32 +# include +# else +# include +# endif +#endif + +static int le_protocols; + +struct php_user_stream_wrapper { + char * protoname; + char * classname; + zend_class_entry *ce; + php_stream_wrapper wrapper; +}; + +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 int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC); +static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC); +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); + +static php_stream_wrapper_ops user_stream_wops = { + user_wrapper_opener, + NULL, /* close - the streams themselves know how */ + NULL, /* stat - the streams themselves know how */ + user_wrapper_stat_url, + user_wrapper_opendir, + "user-space", + user_wrapper_unlink, + user_wrapper_rename, + user_wrapper_mkdir, + user_wrapper_rmdir, + user_wrapper_metadata +}; + + +static void stream_wrapper_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr; + + efree(uwrap->protoname); + efree(uwrap->classname); + efree(uwrap); +} + + +PHP_MINIT_FUNCTION(user_streams) +{ + le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0); + if (le_protocols == FAILURE) + return FAILURE; + + 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_REPORT_ERRORS", REPORT_ERRORS, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_MUST_SEEK", STREAM_MUST_SEEK, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_URL_STAT_LINK", PHP_STREAM_URL_STAT_LINK, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_URL_STAT_QUIET", PHP_STREAM_URL_STAT_QUIET, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE", PHP_STREAM_MKDIR_RECURSIVE, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_IS_URL", PHP_STREAM_IS_URL, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_OPTION_BLOCKING", PHP_STREAM_OPTION_BLOCKING, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_TIMEOUT", PHP_STREAM_OPTION_READ_TIMEOUT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_BUFFER", PHP_STREAM_OPTION_READ_BUFFER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_WRITE_BUFFER", PHP_STREAM_OPTION_WRITE_BUFFER, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_BUFFER_NONE", PHP_STREAM_BUFFER_NONE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_BUFFER_LINE", PHP_STREAM_BUFFER_LINE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_BUFFER_FULL", PHP_STREAM_BUFFER_FULL, CONST_CS|CONST_PERSISTENT); + + 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; +} + +struct _php_userstream_data { + struct php_user_stream_wrapper * wrapper; + zval * object; +}; +typedef struct _php_userstream_data php_userstream_data_t; + +/* names of methods */ +#define USERSTREAM_OPEN "stream_open" +#define USERSTREAM_CLOSE "stream_close" +#define USERSTREAM_READ "stream_read" +#define USERSTREAM_WRITE "stream_write" +#define USERSTREAM_FLUSH "stream_flush" +#define USERSTREAM_SEEK "stream_seek" +#define USERSTREAM_TELL "stream_tell" +#define USERSTREAM_EOF "stream_eof" +#define USERSTREAM_STAT "stream_stat" +#define USERSTREAM_STATURL "url_stat" +#define USERSTREAM_UNLINK "unlink" +#define USERSTREAM_RENAME "rename" +#define USERSTREAM_MKDIR "mkdir" +#define USERSTREAM_RMDIR "rmdir" +#define USERSTREAM_DIR_OPEN "dir_opendir" +#define USERSTREAM_DIR_READ "dir_readdir" +#define USERSTREAM_DIR_REWIND "dir_rewinddir" +#define USERSTREAM_DIR_CLOSE "dir_closedir" +#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; + } + + function stream_tell() + { + return (int)$position; + } + + function stream_eof() + { + return true/false; + } + + function stream_stat() + { + return array( just like that returned by fstat() ); + } + + function stream_cast($castas) + { + if ($castas == STREAM_CAST_FOR_SELECT) { + return $this->underlying_stream; + } + return false; + } + + function stream_set_option($option, $arg1, $arg2) + { + switch($option) { + case STREAM_OPTION_BLOCKING: + $blocking = $arg1; + ... + case STREAM_OPTION_READ_TIMEOUT: + $sec = $arg1; + $usec = $arg2; + ... + case STREAM_OPTION_WRITE_BUFFER: + $mode = $arg1; + $size = $arg2; + ... + default: + return false; + } + } + + function url_stat(string $url, int $flags) + { + return array( just like that returned by stat() ); + } + + function unlink(string $url) + { + return true / false; + } + + function rename(string $from, string $to) + { + return true / false; + } + + function mkdir($dir, $mode, $options) + { + return true / false; + } + + function rmdir($dir, $options) + { + return true / false; + } + + function dir_opendir(string $url, int $options) + { + return true / false; + } + + function dir_readdir() + { + return string next filename in dir ; + } + + function dir_closedir() + { + release dir related resources; + } + + function dir_rewinddir() + { + reset to start of dir list; + } + + function stream_lock($operation) + { + return true / false; + } + + function stream_truncate($new_size) + { + return true / false; + } + + }}} **/ + +static zval *user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context TSRMLS_DC) +{ + 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); + + if (context) { + add_property_resource(object, "context", context->rsrc_id); + zend_list_addref(context->rsrc_id); + } else { + add_property_null(object, "context"); + } + + 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 = 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(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(object); + FREE_ZVAL(object); + return NULL; + } else { + if (retval_ptr) { + zval_ptr_dtor(&retval_ptr); + } + } + } + return object; +} + +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); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zmode); + ZVAL_STRING(zmode, mode, 1); + args[1] = &zmode; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[2] = &zoptions; + + MAKE_STD_ZVAL(zopened); + Z_SET_REFCOUNT_P(zopened, 1); + Z_SET_ISREF_P(zopened); + ZVAL_NULL(zopened); + args[3] = &zopened; + + 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); + + /* if the opened path is set, copy it out */ + if (Z_TYPE_P(zopened) == IS_STRING && opened_path) { + *opened_path = estrndup(Z_STRVAL_P(zopened), Z_STRLEN_P(zopened)); + } + + /* set wrapper data to be a reference to our object */ + stream->wrapperdata = us->object; + zval_add_ref(&stream->wrapperdata); + } else { + 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); + efree(us); + } + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zopened); + zval_ptr_dtor(&zoptions); + zval_ptr_dtor(&zmode); + zval_ptr_dtor(&zfilename); + + FG(user_stream_current_filename) = NULL; + + PG(in_user_include) = old_in_user_include; + return stream; +} + +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) +{ + 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]; + int call_result; + php_stream *stream = NULL; + + /* 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; + + 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; + efree(us); + return NULL; + } + + /* call it's dir_open method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, filename, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[1] = &zoptions; + + 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); + + /* set wrapper data to be a reference to our object */ + stream->wrapperdata = us->object; + zval_add_ref(&stream->wrapperdata); + } else { + 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); + efree(us); + } + 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; +} + + +/* {{{ proto bool stream_wrapper_register(string protocol, string classname[, integer flags]) + Registers a custom URL protocol handler class */ +PHP_FUNCTION(stream_wrapper_register) +{ + char *protocol, *classname; + int protocol_len, classname_len; + 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); + uwrap->wrapper.wops = &user_stream_wops; + uwrap->wrapper.abstract = uwrap; + uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0); + + rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols); + + if (zend_lookup_class(uwrap->classname, classname_len, (zend_class_entry***)&uwrap->ce TSRMLS_CC) == SUCCESS) { + uwrap->ce = *(zend_class_entry**)uwrap->ce; + if (php_register_url_stream_wrapper_volatile(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) { + RETURN_TRUE; + } else { + /* We failed. But why? */ + if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol, protocol_len + 1)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Protocol %s:// is already defined.", protocol); + } else { + /* Hash doesn't exist so it must have been an invalid protocol scheme */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", classname, protocol); + } + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "class '%s' is undefined", classname); + } + + zend_list_delete(rsrc_id); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto bool stream_wrapper_unregister(string protocol) + Unregister a wrapper for the life of the current request. */ +PHP_FUNCTION(stream_wrapper_unregister) +{ + char *protocol; + int protocol_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) { + RETURN_FALSE; + } + + if (php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC) == FAILURE) { + /* We failed */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to unregister protocol %s://", protocol); + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool stream_wrapper_restore(string protocol) + Restore the original protocol handler, overriding if necessary */ +PHP_FUNCTION(stream_wrapper_restore) +{ + char *protocol; + int protocol_len; + php_stream_wrapper **wrapperpp = NULL, *wrapper; + HashTable *global_wrapper_hash; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &protocol, &protocol_len) == FAILURE) { + RETURN_FALSE; + } + + global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global(); + if (php_stream_get_url_stream_wrappers_hash() == global_wrapper_hash) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s:// was never changed, nothing to restore", protocol); + RETURN_TRUE; + } + + if ((zend_hash_find(global_wrapper_hash, protocol, protocol_len + 1, (void**)&wrapperpp) == FAILURE) || !wrapperpp) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// never existed, nothing to restore", protocol); + RETURN_FALSE; + } + + /* next line might delete the pointer that wrapperpp points at, so deref it now */ + wrapper = *wrapperpp; + + /* A failure here could be okay given that the protocol might have been merely unregistered */ + php_unregister_url_stream_wrapper_volatile(protocol TSRMLS_CC); + + 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; +} +/* }}} */ + +static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval **args[1]; + zval *zbufptr; + size_t didwrite = 0; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0); + + MAKE_STD_ZVAL(zbufptr); + ZVAL_STRINGL(zbufptr, (char*)buf, count, 1);; + args[0] = &zbufptr; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + zval_ptr_dtor(&zbufptr); + + didwrite = 0; + if (call_result == SUCCESS && retval != NULL) { + convert_to_long(retval); + didwrite = Z_LVAL_P(retval); + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!", + us->wrapper->classname); + } + + /* don't allow strange buffer overruns due to bogus return */ + if (didwrite > count) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_WRITE " wrote %ld bytes more data than requested (%ld written, %ld max)", + us->wrapper->classname, + (long)(didwrite - count), (long)didwrite, (long)count); + didwrite = count; + } + + if (retval) + zval_ptr_dtor(&retval); + + return didwrite; +} + +static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + zval **args[1]; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval *zcount; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0); + + MAKE_STD_ZVAL(zcount); + ZVAL_LONG(zcount, count); + args[0] = &zcount; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL) { + convert_to_string(retval); + didread = Z_STRLEN_P(retval); + if (didread > count) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " - read %ld bytes more data than requested (%ld read, %ld max) - excess data will be lost", + us->wrapper->classname, (long)(didread - count), (long)didread, (long)count); + didread = count; + } + if (didread > 0) + memcpy(buf, Z_STRVAL_P(retval), didread); + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!", + us->wrapper->classname); + } + zval_ptr_dtor(&zcount); + + if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + + /* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */ + + ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) { + stream->eof = 1; + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", + us->wrapper->classname); + + stream->eof = 1; + } + + if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + + return didread; +} + +static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + 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, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + zval_ptr_dtor(&us->object); + + efree(us); + + return 0; +} + +static int php_userstreamop_flush(php_stream *stream TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + 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, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) + call_result = 0; + else + call_result = -1; + + if (retval) + zval_ptr_dtor(&retval); + + return call_result; +} + +static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result, ret; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval **args[2]; + zval *zoffs, *zwhence; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0); + + MAKE_STD_ZVAL(zoffs); + ZVAL_LONG(zoffs, offset); + args[0] = &zoffs; + + MAKE_STD_ZVAL(zwhence); + ZVAL_LONG(zwhence, whence); + args[1] = &zwhence; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 2, args, + 0, NULL TSRMLS_CC); + + zval_ptr_dtor(&zoffs); + zval_ptr_dtor(&zwhence); + + if (call_result == FAILURE) { + /* 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) + zval_ptr_dtor(&retval); + + return -1; + } else if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) { + ret = 0; + } else { + ret = -1; + } + + if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + + if (ret) { + return ret; + } + + /* now determine where we are */ + ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) { + *newoffs = Z_LVAL_P(retval); + ret = 0; + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", us->wrapper->classname); + ret = -1; + } else { + ret = -1; + } + + if (retval) { + zval_ptr_dtor(&retval); + } + return ret; +} + +/* parse the return value from one of the stat functions and store the + * relevant fields into the statbuf provided */ +static int statbuf_from_array(zval *array, php_stream_statbuf *ssb TSRMLS_DC) +{ + zval **elem; + +#define STAT_PROP_ENTRY_EX(name, name2) \ + if (SUCCESS == zend_hash_find(Z_ARRVAL_P(array), #name, sizeof(#name), (void**)&elem)) { \ + SEPARATE_ZVAL(elem); \ + convert_to_long(*elem); \ + ssb->sb.st_##name2 = Z_LVAL_PP(elem); \ + } + +#define STAT_PROP_ENTRY(name) STAT_PROP_ENTRY_EX(name,name) + + memset(ssb, 0, sizeof(php_stream_statbuf)); + STAT_PROP_ENTRY(dev); + STAT_PROP_ENTRY(ino); + STAT_PROP_ENTRY(mode); + STAT_PROP_ENTRY(nlink); + STAT_PROP_ENTRY(uid); + STAT_PROP_ENTRY(gid); +#if HAVE_ST_RDEV + STAT_PROP_ENTRY(rdev); +#endif + STAT_PROP_ENTRY(size); +#ifdef NETWARE + STAT_PROP_ENTRY_EX(atime, atime.tv_sec); + STAT_PROP_ENTRY_EX(mtime, mtime.tv_sec); + STAT_PROP_ENTRY_EX(ctime, ctime.tv_sec); +#else + STAT_PROP_ENTRY(atime); + STAT_PROP_ENTRY(mtime); + STAT_PROP_ENTRY(ctime); +#endif +#ifdef HAVE_ST_BLKSIZE + STAT_PROP_ENTRY(blksize); +#endif +#ifdef HAVE_ST_BLOCKS + STAT_PROP_ENTRY(blocks); +#endif + +#undef STAT_PROP_ENTRY +#undef STAT_PROP_ENTRY_EX + return SUCCESS; +} + +static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + int ret = -1; + + ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_ARRAY) { + if (SUCCESS == statbuf_from_array(retval, ssb TSRMLS_CC)) + ret = 0; + } else { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!", + us->wrapper->classname); + } + } + + if (retval) + zval_ptr_dtor(&retval); + + return ret; +} + + +static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) { + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL; + zval *zvalue = NULL; + zval **args[3]; + + switch (option) { + case PHP_STREAM_OPTION_CHECK_LIVENESS: + ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0); + call_result = call_user_function_ex(NULL, &us->object, &func_name, &retval, 0, NULL, 0, NULL TSRMLS_CC); + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_BOOL) { + ret = zval_is_true(retval) ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; + } else { + ret = PHP_STREAM_OPTION_RETURN_ERR; + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", + us->wrapper->classname); + } + break; + + case PHP_STREAM_OPTION_LOCKING: + MAKE_STD_ZVAL(zvalue); + ZVAL_LONG(zvalue, 0); + + if (value & LOCK_NB) { + Z_LVAL_P(zvalue) |= PHP_LOCK_NB; + } + switch(value & ~LOCK_NB) { + case LOCK_SH: + Z_LVAL_P(zvalue) |= PHP_LOCK_SH; + break; + case LOCK_EX: + Z_LVAL_P(zvalue) |= PHP_LOCK_EX; + break; + case LOCK_UN: + Z_LVAL_P(zvalue) |= PHP_LOCK_UN; + break; + } + + 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) { + /* lock support test (TODO: more check) */ + ret = PHP_STREAM_OPTION_RETURN_OK; + } else { + 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); + ZVAL_LONG(zoption, option); + + ALLOC_INIT_ZVAL(zvalue); + ALLOC_INIT_ZVAL(zptrparam); + + args[0] = &zoption; + args[1] = &zvalue; + args[2] = &zptrparam; + + switch(option) { + case PHP_STREAM_OPTION_READ_BUFFER: + case PHP_STREAM_OPTION_WRITE_BUFFER: + ZVAL_LONG(zvalue, value); + if (ptrparam) { + ZVAL_LONG(zptrparam, *(long *)ptrparam); + } else { + ZVAL_LONG(zptrparam, BUFSIZ); + } + break; + case PHP_STREAM_OPTION_READ_TIMEOUT: { + struct timeval tv = *(struct timeval*)ptrparam; + ZVAL_LONG(zvalue, tv.tv_sec); + ZVAL_LONG(zptrparam, tv.tv_usec); + break; + } + case PHP_STREAM_OPTION_BLOCKING: + ZVAL_LONG(zvalue, value); + break; + default: + break; + } + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 3, args, 0, NULL TSRMLS_CC); + + 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); + } + if (zptrparam) { + zval_ptr_dtor(&zptrparam); + } + + break; + } + } + + /* clean up */ + if (retval) { + zval_ptr_dtor(&retval); + } + + + if (zvalue) { + zval_ptr_dtor(&zvalue); + } + + return ret; +} + + +static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zval *zfilename, *zfuncname, *zretval; + zval **args[1]; + int call_result; + zval *object; + int ret = 0; + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; + } + + /* call the unlink method */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_UNLINK, 1); + + call_result = call_user_function_ex(NULL, + &object, + zfuncname, + &zretval, + 1, 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_UNLINK " 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); + + return ret; +} + +static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zval *zold_name, *znew_name, *zfuncname, *zretval; + zval **args[2]; + int call_result; + zval *object; + int ret = 0; + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; + } + + /* call the rename method */ + MAKE_STD_ZVAL(zold_name); + ZVAL_STRING(zold_name, url_from, 1); + args[0] = &zold_name; + + MAKE_STD_ZVAL(znew_name); + ZVAL_STRING(znew_name, url_to, 1); + args[1] = &znew_name; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_RENAME, 1); + + call_result = call_user_function_ex(NULL, + &object, + zfuncname, + &zretval, + 2, 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_RENAME " is not implemented!", uwrap->classname); + } + + /* clean up */ + zval_ptr_dtor(&object); + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zold_name); + zval_ptr_dtor(&znew_name); + + return ret; +} + +static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int options, php_stream_context *context TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zval *zfilename, *zmode, *zoptions, *zfuncname, *zretval; + zval **args[3]; + int call_result; + zval *object; + int ret = 0; + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; + } + + /* call the mkdir method */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zmode); + ZVAL_LONG(zmode, mode); + args[1] = &zmode; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[2] = &zoptions; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_MKDIR, 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_MKDIR " 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(&zmode); + zval_ptr_dtor(&zoptions); + + return ret; +} + +static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zval *zfilename, *zoptions, *zfuncname, *zretval; + zval **args[3]; + int call_result; + zval *object; + int ret = 0; + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; + } + + /* call the rmdir method */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[1] = &zoptions; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_RMDIR, 1); + + call_result = call_user_function_ex(NULL, + &object, + zfuncname, + &zretval, + 2, 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_RMDIR " 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(&zoptions); + + 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]; + int call_result; + zval *object; + int ret = -1; + + /* create an instance of our class */ + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; + } + + /* call it's stat_url method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, url, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zflags); + ZVAL_LONG(zflags, flags); + args[1] = &zflags; + + 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)) + ret = 0; + } else { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_STATURL " 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(&zflags); + + return ret; + +} + +static size_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + php_stream_dirent *ent = (php_stream_dirent*)buf; + + /* avoid problems if someone mis-uses the stream */ + if (count != sizeof(php_stream_dirent)) + return 0; + + ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) != IS_BOOL) { + convert_to_string(retval); + PHP_STRLCPY(ent->d_name, Z_STRVAL_P(retval), sizeof(ent->d_name), Z_STRLEN_P(retval)); + + didread = sizeof(php_stream_dirent); + } else if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!", + us->wrapper->classname); + } + + if (retval) + zval_ptr_dtor(&retval); + + return didread; +} + +static int php_userstreamop_closedir(php_stream *stream, int close_handle TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + 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, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + zval_ptr_dtor(&us->object); + + efree(us); + + return 0; +} + +static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int whence, off_t *newoffs TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + 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, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + return 0; + +} + +static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr TSRMLS_DC) +{ + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval func_name; + zval *retval = NULL; + zval *zcastas = NULL; + zval **args[1]; + php_stream * intstream = NULL; + int call_result; + int ret = FAILURE; + + ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1, 0); + + ALLOC_INIT_ZVAL(zcastas); + switch(castas) { + case PHP_STREAM_AS_FD_FOR_SELECT: + ZVAL_LONG(zcastas, PHP_STREAM_AS_FD_FOR_SELECT); + break; + default: + ZVAL_LONG(zcastas, PHP_STREAM_AS_STDIO); + break; + } + args[0] = &zcastas; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, 0, NULL TSRMLS_CC); + + do { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + us->wrapper->classname); + break; + } + if (retval == NULL || !zend_is_true(retval)) { + break; + } + php_stream_from_zval_no_verify(intstream, &retval); + if (!intstream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + us->wrapper->classname); + break; + } + if (intstream == stream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + us->wrapper->classname); + intstream = NULL; + break; + } + ret = php_stream_cast(intstream, castas, retptr, 1); + } while (0); + + if (retval) { + zval_ptr_dtor(&retval); + } + if (zcastas) { + zval_ptr_dtor(&zcastas); + } + + return ret; +} + +php_stream_ops php_stream_userspace_ops = { + php_userstreamop_write, php_userstreamop_read, + php_userstreamop_close, php_userstreamop_flush, + "user-space", + php_userstreamop_seek, + php_userstreamop_cast, + php_userstreamop_stat, + php_userstreamop_set_option, +}; + +php_stream_ops php_stream_userspace_dir_ops = { + NULL, /* write */ + php_userstreamop_readdir, + php_userstreamop_closedir, + NULL, /* flush */ + "user-space-dir", + php_userstreamop_rewinddir, + NULL, /* cast */ + NULL, /* stat */ + NULL /* set_option */ +}; + + diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c new file mode 100644 index 0000000..beffc73 --- /dev/null +++ b/main/streams/xp_socket.c @@ -0,0 +1,838 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Wez Furlong | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "php.h" +#include "ext/standard/file.h" +#include "streams/php_streams_int.h" +#include "php_network.h" + +#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE) +# undef AF_UNIX +#endif + +#if defined(AF_UNIX) +#include +#endif + +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif + +#ifndef MSG_PEEK +# define MSG_PEEK 0 +#endif + +php_stream_ops php_stream_generic_socket_ops; +PHPAPI php_stream_ops php_stream_socket_ops; +php_stream_ops php_stream_udp_socket_ops; +#ifdef AF_UNIX +php_stream_ops php_stream_unix_socket_ops; +php_stream_ops php_stream_unixdg_socket_ops; +#endif + + +static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC); + +/* {{{ Generic socket stream operations */ +static size_t php_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + int didwrite; + struct timeval *ptimeout; + + if (sock->socket == -1) { + return 0; + } + + if (sock->timeout.tv_sec == -1) + ptimeout = NULL; + else + ptimeout = &sock->timeout; + +retry: + didwrite = send(sock->socket, buf, count, (sock->is_blocked && ptimeout) ? MSG_DONTWAIT : 0); + + if (didwrite <= 0) { + long err = php_socket_errno(); + char *estr; + + if (sock->is_blocked && err == EWOULDBLOCK) { + int retval; + + sock->timeout_event = 0; + + do { + retval = php_pollfd_for(sock->socket, POLLOUT, ptimeout); + + if (retval == 0) { + sock->timeout_event = 1; + break; + } + + if (retval > 0) { + /* writable now; retry */ + goto retry; + } + + err = php_socket_errno(); + } while (err == EINTR); + } + estr = php_socket_strerror(err, NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "send of %ld bytes failed with errno=%ld %s", + (long)count, err, estr); + efree(estr); + } + + if (didwrite > 0) { + php_stream_notify_progress_increment(stream->context, didwrite, 0); + } + + if (didwrite < 0) { + didwrite = 0; + } + + return didwrite; +} + +static void php_sock_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC) +{ + int retval; + struct timeval *ptimeout; + + if (sock->socket == -1) { + return; + } + + sock->timeout_event = 0; + + if (sock->timeout.tv_sec == -1) + ptimeout = NULL; + else + ptimeout = &sock->timeout; + + while(1) { + retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout); + + if (retval == 0) + sock->timeout_event = 1; + + if (retval >= 0) + break; + + if (php_socket_errno() != EINTR) + break; + } +} + +static size_t php_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + int nr_bytes = 0; + + if (sock->socket == -1) { + return 0; + } + + if (sock->is_blocked) { + php_sock_stream_wait_for_data(stream, sock TSRMLS_CC); + if (sock->timeout_event) + return 0; + } + + nr_bytes = recv(sock->socket, buf, count, (sock->is_blocked && sock->timeout.tv_sec != -1) ? MSG_DONTWAIT : 0); + + stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && php_socket_errno() != EWOULDBLOCK)); + + if (nr_bytes > 0) { + php_stream_notify_progress_increment(stream->context, nr_bytes, 0); + } + + if (nr_bytes < 0) { + nr_bytes = 0; + } + + return nr_bytes; +} + + +static int php_sockop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; +#ifdef PHP_WIN32 + int n; +#endif + + if (close_handle) { + +#ifdef PHP_WIN32 + if (sock->socket == -1) + sock->socket = SOCK_ERR; +#endif + if (sock->socket != SOCK_ERR) { +#ifdef PHP_WIN32 + /* prevent more data from coming in */ + shutdown(sock->socket, SHUT_RD); + + /* try to make sure that the OS sends all data before we close the connection. + * Essentially, we are waiting for the socket to become writeable, which means + * that all pending data has been sent. + * We use a small timeout which should encourage the OS to send the data, + * but at the same time avoid hanging indefintely. + * */ + do { + n = php_pollfd_for_ms(sock->socket, POLLOUT, 500); + } while (n == -1 && php_socket_errno() == EINTR); +#endif + closesocket(sock->socket); + sock->socket = SOCK_ERR; + } + + } + + pefree(sock, php_stream_is_persistent(stream)); + + return 0; +} + +static int php_sockop_flush(php_stream *stream TSRMLS_DC) +{ +#if 0 + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + return fsync(sock->socket); +#endif + return 0; +} + +static int php_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; +#if ZEND_WIN32 + return 0; +#else + return fstat(sock->socket, &ssb->sb); +#endif +} + +static inline int sock_sendto(php_netstream_data_t *sock, char *buf, size_t buflen, int flags, + struct sockaddr *addr, socklen_t addrlen + TSRMLS_DC) +{ + int ret; + if (addr) { + ret = sendto(sock->socket, buf, buflen, flags, addr, addrlen); + return (ret == SOCK_CONN_ERR) ? -1 : ret; + } + return ((ret = send(sock->socket, buf, buflen, flags)) == SOCK_CONN_ERR) ? -1 : ret; +} + +static inline int sock_recvfrom(php_netstream_data_t *sock, char *buf, size_t buflen, int flags, + char **textaddr, long *textaddrlen, + struct sockaddr **addr, socklen_t *addrlen + TSRMLS_DC) +{ + php_sockaddr_storage sa; + socklen_t sl = sizeof(sa); + int ret; + int want_addr = textaddr || addr; + + if (want_addr) { + ret = recvfrom(sock->socket, buf, buflen, flags, (struct sockaddr*)&sa, &sl); + ret = (ret == SOCK_CONN_ERR) ? -1 : ret; + php_network_populate_name_from_sockaddr((struct sockaddr*)&sa, sl, + textaddr, textaddrlen, addr, addrlen TSRMLS_CC); + } else { + ret = recv(sock->socket, buf, buflen, flags); + ret = (ret == SOCK_CONN_ERR) ? -1 : ret; + } + + return ret; +} + +static int php_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + int oldmode, flags; + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + php_stream_xport_param *xparam; + + switch(option) { + case PHP_STREAM_OPTION_CHECK_LIVENESS: + { + struct timeval tv; + char buf; + int alive = 1; + + if (value == -1) { + if (sock->timeout.tv_sec == -1) { + tv.tv_sec = FG(default_socket_timeout); + tv.tv_usec = 0; + } else { + tv = sock->timeout; + } + } else { + tv.tv_sec = value; + tv.tv_usec = 0; + } + + if (sock->socket == -1) { + alive = 0; + } else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) { + if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) { + alive = 0; + } + } + return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + + case PHP_STREAM_OPTION_BLOCKING: + oldmode = sock->is_blocked; + if (SUCCESS == php_set_sock_blocking(sock->socket, value TSRMLS_CC)) { + sock->is_blocked = value; + return oldmode; + } + return PHP_STREAM_OPTION_RETURN_ERR; + + case PHP_STREAM_OPTION_READ_TIMEOUT: + sock->timeout = *(struct timeval*)ptrparam; + sock->timeout_event = 0; + return PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_OPTION_META_DATA_API: + add_assoc_bool((zval *)ptrparam, "timed_out", sock->timeout_event); + add_assoc_bool((zval *)ptrparam, "blocked", sock->is_blocked); + add_assoc_bool((zval *)ptrparam, "eof", stream->eof); + return PHP_STREAM_OPTION_RETURN_OK; + + case PHP_STREAM_OPTION_XPORT_API: + xparam = (php_stream_xport_param *)ptrparam; + + switch (xparam->op) { + case STREAM_XPORT_OP_LISTEN: + xparam->outputs.returncode = (listen(sock->socket, xparam->inputs.backlog) == 0) ? 0: -1; + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_GET_NAME: + xparam->outputs.returncode = php_network_get_sock_name(sock->socket, + xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, + xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, + xparam->want_addr ? &xparam->outputs.addr : NULL, + xparam->want_addr ? &xparam->outputs.addrlen : NULL + TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_GET_PEER_NAME: + xparam->outputs.returncode = php_network_get_peer_name(sock->socket, + xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, + xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, + xparam->want_addr ? &xparam->outputs.addr : NULL, + xparam->want_addr ? &xparam->outputs.addrlen : NULL + TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_SEND: + flags = 0; + if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) { + flags |= MSG_OOB; + } + xparam->outputs.returncode = sock_sendto(sock, + xparam->inputs.buf, xparam->inputs.buflen, + flags, + xparam->inputs.addr, + xparam->inputs.addrlen TSRMLS_CC); + if (xparam->outputs.returncode == -1) { + char *err = php_socket_strerror(php_socket_errno(), NULL, 0); + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "%s\n", err); + efree(err); + } + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_RECV: + flags = 0; + if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) { + flags |= MSG_OOB; + } + if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) { + flags |= MSG_PEEK; + } + xparam->outputs.returncode = sock_recvfrom(sock, + xparam->inputs.buf, xparam->inputs.buflen, + flags, + xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, + xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, + xparam->want_addr ? &xparam->outputs.addr : NULL, + xparam->want_addr ? &xparam->outputs.addrlen : NULL + TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + + +#ifdef HAVE_SHUTDOWN +# ifndef SHUT_RD +# define SHUT_RD 0 +# endif +# ifndef SHUT_WR +# define SHUT_WR 1 +# endif +# ifndef SHUT_RDWR +# define SHUT_RDWR 2 +# endif + case STREAM_XPORT_OP_SHUTDOWN: { + static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR}; + + xparam->outputs.returncode = shutdown(sock->socket, shutdown_how[xparam->how]); + return PHP_STREAM_OPTION_RETURN_OK; + } +#endif + + default: + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + } + + default: + return PHP_STREAM_OPTION_RETURN_NOTIMPL; + } +} + +static int php_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + + switch(castas) { + case PHP_STREAM_AS_STDIO: + if (ret) { + *(FILE**)ret = fdopen(sock->socket, stream->mode); + if (*ret) + return SUCCESS; + return FAILURE; + } + return SUCCESS; + case PHP_STREAM_AS_FD_FOR_SELECT: + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: + if (ret) + *(int*)ret = sock->socket; + return SUCCESS; + default: + return FAILURE; + } +} +/* }}} */ + +/* These may look identical, but we need them this way so that + * we can determine which type of socket we are dealing with + * by inspecting stream->ops. + * A "useful" side-effect is that the user's scripts can then + * make similar decisions using stream_get_meta_data. + * */ +php_stream_ops php_stream_generic_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + "generic_socket", + NULL, /* seek */ + php_sockop_cast, + php_sockop_stat, + php_sockop_set_option, +}; + + +php_stream_ops php_stream_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + "tcp_socket", + NULL, /* seek */ + php_sockop_cast, + php_sockop_stat, + php_tcp_sockop_set_option, +}; + +php_stream_ops php_stream_udp_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + "udp_socket", + NULL, /* seek */ + php_sockop_cast, + php_sockop_stat, + php_tcp_sockop_set_option, +}; + +#ifdef AF_UNIX +php_stream_ops php_stream_unix_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + "unix_socket", + NULL, /* seek */ + php_sockop_cast, + php_sockop_stat, + php_tcp_sockop_set_option, +}; +php_stream_ops php_stream_unixdg_socket_ops = { + php_sockop_write, php_sockop_read, + php_sockop_close, php_sockop_flush, + "udg_socket", + NULL, /* seek */ + php_sockop_cast, + php_sockop_stat, + php_tcp_sockop_set_option, +}; +#endif + + +/* network socket operations */ + +#ifdef AF_UNIX +static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr TSRMLS_DC) +{ + memset(unix_addr, 0, sizeof(*unix_addr)); + unix_addr->sun_family = AF_UNIX; + + /* we need to be binary safe on systems that support an abstract + * namespace */ + if (xparam->inputs.namelen >= sizeof(unix_addr->sun_path)) { + /* On linux, when the path begins with a NUL byte we are + * referring to an abstract namespace. In theory we should + * allow an extra byte below, since we don't need the NULL. + * BUT, to get into this branch of code, the name is too long, + * so we don't care. */ + xparam->inputs.namelen = sizeof(unix_addr->sun_path) - 1; + php_error_docref(NULL TSRMLS_CC, E_NOTICE, + "socket path exceeded the maximum allowed length of %lu bytes " + "and was truncated", (unsigned long)sizeof(unix_addr->sun_path)); + } + + memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen); + + return 1; +} +#endif + +static inline char *parse_ip_address_ex(const char *str, int str_len, int *portno, int get_err, char **err TSRMLS_DC) +{ + char *colon; + char *host = NULL; + +#ifdef HAVE_IPV6 + char *p; + + if (*(str) == '[' && str_len > 1) { + /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ + p = memchr(str + 1, ']', str_len - 2); + if (!p || *(p + 1) != ':') { + if (get_err) { + spprintf(err, 0, "Failed to parse IPv6 address \"%s\"", str); + } + return NULL; + } + *portno = atoi(p + 2); + return estrndup(str + 1, p - str - 1); + } +#endif + if (str_len) { + colon = memchr(str, ':', str_len - 1); + } else { + colon = NULL; + } + if (colon) { + *portno = atoi(colon + 1); + host = estrndup(str, colon - str); + } else { + if (get_err) { + spprintf(err, 0, "Failed to parse address \"%s\"", str); + } + return NULL; + } + + return host; +} + +static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno TSRMLS_DC) +{ + return parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, portno, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC); +} + +static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t *sock, + php_stream_xport_param *xparam TSRMLS_DC) +{ + char *host = NULL; + int portno, err; + +#ifdef AF_UNIX + if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) { + struct sockaddr_un unix_addr; + + sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (sock->socket == SOCK_ERR) { + if (xparam->want_errortext) { + spprintf(&xparam->outputs.error_text, 0, "Failed to create unix%s socket %s", + stream->ops == &php_stream_unix_socket_ops ? "" : "datagram", + strerror(errno)); + } + return -1; + } + + parse_unix_address(xparam, &unix_addr TSRMLS_CC); + + return bind(sock->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr)); + } +#endif + + host = parse_ip_address(xparam, &portno TSRMLS_CC); + + if (host == NULL) { + return -1; + } + + sock->socket = php_network_bind_socket_to_local_addr(host, portno, + stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM, + xparam->want_errortext ? &xparam->outputs.error_text : NULL, + &err + TSRMLS_CC); + + if (host) { + efree(host); + } + + return sock->socket == -1 ? -1 : 0; +} + +static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_t *sock, + php_stream_xport_param *xparam TSRMLS_DC) +{ + char *host = NULL, *bindto = NULL; + int portno, bindport = 0; + int err = 0; + int ret; + zval **tmpzval = NULL; + +#ifdef AF_UNIX + if (stream->ops == &php_stream_unix_socket_ops || stream->ops == &php_stream_unixdg_socket_ops) { + struct sockaddr_un unix_addr; + + sock->socket = socket(PF_UNIX, stream->ops == &php_stream_unix_socket_ops ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (sock->socket == SOCK_ERR) { + if (xparam->want_errortext) { + spprintf(&xparam->outputs.error_text, 0, "Failed to create unix socket"); + } + return -1; + } + + parse_unix_address(xparam, &unix_addr TSRMLS_CC); + + ret = php_network_connect_socket(sock->socket, + (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen, + xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout, + xparam->want_errortext ? &xparam->outputs.error_text : NULL, + &err); + + xparam->outputs.error_code = err; + + goto out; + } +#endif + + host = parse_ip_address(xparam, &portno TSRMLS_CC); + + if (host == NULL) { + return -1; + } + + if (stream->context && php_stream_context_get_option(stream->context, "socket", "bindto", &tmpzval) == SUCCESS) { + if (Z_TYPE_PP(tmpzval) != IS_STRING) { + if (xparam->want_errortext) { + spprintf(&xparam->outputs.error_text, 0, "local_addr context option is not a string."); + } + efree(host); + return -1; + } + bindto = parse_ip_address_ex(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text TSRMLS_CC); + } + + /* Note: the test here for php_stream_udp_socket_ops is important, because we + * want the default to be TCP sockets so that the openssl extension can + * re-use this code. */ + + sock->socket = php_network_connect_socket_to_host(host, portno, + stream->ops == &php_stream_udp_socket_ops ? SOCK_DGRAM : SOCK_STREAM, + xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, + xparam->inputs.timeout, + xparam->want_errortext ? &xparam->outputs.error_text : NULL, + &err, + bindto, + bindport + TSRMLS_CC); + + ret = sock->socket == -1 ? -1 : 0; + xparam->outputs.error_code = err; + + if (host) { + efree(host); + } + if (bindto) { + efree(bindto); + } + +#ifdef AF_UNIX +out: +#endif + + if (ret >= 0 && xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && err == EINPROGRESS) { + /* indicates pending connection */ + return 1; + } + + return ret; +} + +static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock, + php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC) +{ + int clisock; + + xparam->outputs.client = NULL; + + clisock = php_network_accept_incoming(sock->socket, + xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, + xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, + xparam->want_addr ? &xparam->outputs.addr : NULL, + xparam->want_addr ? &xparam->outputs.addrlen : NULL, + xparam->inputs.timeout, + xparam->want_errortext ? &xparam->outputs.error_text : NULL, + &xparam->outputs.error_code + TSRMLS_CC); + + if (clisock >= 0) { + php_netstream_data_t *clisockdata; + + clisockdata = emalloc(sizeof(*clisockdata)); + + if (clisockdata == NULL) { + close(clisock); + /* technically a fatal error */ + } else { + memcpy(clisockdata, sock, sizeof(*clisockdata)); + clisockdata->socket = clisock; + + xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+"); + if (xparam->outputs.client) { + xparam->outputs.client->context = stream->context; + if (stream->context) { + zend_list_addref(stream->context->rsrc_id); + } + } + } + } + + return xparam->outputs.client == NULL ? -1 : 0; +} + +static int php_tcp_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) +{ + php_netstream_data_t *sock = (php_netstream_data_t*)stream->abstract; + php_stream_xport_param *xparam; + + switch(option) { + case PHP_STREAM_OPTION_XPORT_API: + xparam = (php_stream_xport_param *)ptrparam; + + switch(xparam->op) { + case STREAM_XPORT_OP_CONNECT: + case STREAM_XPORT_OP_CONNECT_ASYNC: + xparam->outputs.returncode = php_tcp_sockop_connect(stream, sock, xparam TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + + case STREAM_XPORT_OP_BIND: + xparam->outputs.returncode = php_tcp_sockop_bind(stream, sock, xparam TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + + + case STREAM_XPORT_OP_ACCEPT: + xparam->outputs.returncode = php_tcp_sockop_accept(stream, sock, xparam STREAMS_CC TSRMLS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + default: + /* fall through */ + ; + } + } + return php_sockop_set_option(stream, option, value, ptrparam TSRMLS_CC); +} + + +PHPAPI php_stream *php_stream_generic_socket_factory(const char *proto, long protolen, + char *resourcename, long resourcenamelen, + const char *persistent_id, int options, int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + php_stream *stream = NULL; + php_netstream_data_t *sock; + php_stream_ops *ops; + + /* which type of socket ? */ + if (strncmp(proto, "tcp", protolen) == 0) { + ops = &php_stream_socket_ops; + } else if (strncmp(proto, "udp", protolen) == 0) { + ops = &php_stream_udp_socket_ops; + } +#ifdef AF_UNIX + else if (strncmp(proto, "unix", protolen) == 0) { + ops = &php_stream_unix_socket_ops; + } else if (strncmp(proto, "udg", protolen) == 0) { + ops = &php_stream_unixdg_socket_ops; + } +#endif + else { + /* should never happen */ + return NULL; + } + + sock = pemalloc(sizeof(php_netstream_data_t), persistent_id ? 1 : 0); + memset(sock, 0, sizeof(php_netstream_data_t)); + + sock->is_blocked = 1; + sock->timeout.tv_sec = FG(default_socket_timeout); + sock->timeout.tv_usec = 0; + + /* we don't know the socket until we have determined if we are binding or + * connecting */ + sock->socket = -1; + + stream = php_stream_alloc_rel(ops, sock, persistent_id, "r+"); + + if (stream == NULL) { + pefree(sock, persistent_id ? 1 : 0); + return NULL; + } + + if (flags == 0) { + return stream; + } + + return stream; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/main/strlcat.c b/main/strlcat.c new file mode 100644 index 0000000..ca22839 --- /dev/null +++ b/main/strlcat.c @@ -0,0 +1,106 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +#include "php.h" + +#ifndef HAVE_STRLCAT + +/* $OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +PHPAPI size_t php_strlcat(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (*d != '\0' && n-- != 0) + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCAT */ + +/* + * 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/strlcpy.c b/main/strlcpy.c new file mode 100644 index 0000000..371faaf --- /dev/null +++ b/main/strlcpy.c @@ -0,0 +1,103 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +#include "php.h" + +#ifndef HAVE_STRLCPY + +/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +PHPAPI size_t php_strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCPY */ + +/* + * 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/win32_internal_function_disabled.h b/main/win32_internal_function_disabled.h new file mode 100644 index 0000000..3a78f6c --- /dev/null +++ b/main/win32_internal_function_disabled.h @@ -0,0 +1,34 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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: Pierre A. Joye | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* 5 means the min version is 5 (XP/2000), 6 (2k8/vista), etc. */ + +/* +Windows Server 2008 6.0 +Windows Vista 6.0 +Windows Server 2003 R2 5.2 +Windows Server 2003 5.2 +Windows XP 5.1 +Windows 2000 5.0 +*/ +static const char *function_name_5[] = {"link", NULL}; +const int function_name_cnt_5 = 1; +static const char *function_name_6[] = {"readlink", "symlink", NULL}; +const int function_name_cnt_6 = 2; diff --git a/main/win95nt.h b/main/win95nt.h new file mode 100644 index 0000000..2e944c2 --- /dev/null +++ b/main/win95nt.h @@ -0,0 +1,89 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 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$ */ + +/* Defines and types for Windows 95/NT */ +#define HAVE_DECLARED_TIMEZONE +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int uid_t; +typedef int gid_t; +typedef char * caddr_t; +#define lstat(x, y) php_sys_lstat(x, y) +#define _IFIFO 0010000 /* fifo */ +#define _IFBLK 0060000 /* block special */ +#define _IFLNK 0120000 /* symbolic link */ +#define S_IFIFO _IFIFO +#define S_IFBLK _IFBLK +#define S_IFLNK _IFLNK +#ifndef S_ISREG +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#define chdir(path) _chdir(path) +#define mkdir(a, b) _mkdir(a) +#define rmdir(a) _rmdir(a) +#define getpid _getpid +#define php_sleep(t) SleepEx(t*1000, TRUE) +#ifndef getcwd +# define getcwd(a, b) _getcwd(a, b) +#endif +#define off_t _off_t +typedef unsigned int uint; +typedef unsigned long ulong; +#if !NSAPI +typedef long pid_t; +#endif + +/* missing in vc5 math.h */ +#define M_PI 3.14159265358979323846 +#define M_TWOPI (M_PI * 2.0) +#define M_PI_2 1.57079632679489661923 +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 +#endif + +#if !defined(PHP_DEBUG) +#ifdef inline +#undef inline +#endif +#define inline __inline +#endif + +/* General Windows stuff */ +#ifndef WINDOWS +# define WINDOWS 1 +#endif + + +/* Prevent use of VC5 OpenFile function */ +#define NOOPENFILE + +/* sendmail is built-in */ +#ifdef PHP_PROG_SENDMAIL +#undef PHP_PROG_SENDMAIL +#define PHP_PROG_SENDMAIL "Built in mailer" +#endif -- cgit v1.2.1