From: Michael Wallner Date: Mon, 28 Sep 2015 14:12:37 +0000 (+0200) Subject: Merge branch 'R_2_5' X-Git-Tag: RELEASE_3_0_0_RC1~19 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=57e944b102006abfa4005337b0ac5901425f4289 Merge branch 'R_2_5' Conflicts: .gitignore config.w32 --- 57e944b102006abfa4005337b0ac5901425f4289 diff --cc .gitignore index c1517d9,fc8d761..34aeda9 --- a/.gitignore +++ b/.gitignore @@@ -40,4 -40,4 +40,5 @@@ tests/*.s lcov_data *~ *.phar +!travis/*.phar + vendor/ diff --cc config9.m4 index c38107c,da682f7..dfad780 --- a/config9.m4 +++ b/config9.m4 @@@ -613,53 -615,17 +615,20 @@@ dnl --- dnl extension deps PHP_ADD_EXTENSION_DEP([http], [raphf], true) PHP_ADD_EXTENSION_DEP([http], [propro], true) - + PHP_SUBST([HTTP_SHARED_LIBADD]) - PHP_HTTP_HEADERS=" - php_http_api.h \ - php_http_buffer.h \ - php_http_curl_client.h \ - php_http_curl_client_datashare.h \ - php_http_client_datashare.h \ - php_http_client_factory.h \ - php_http_client.h \ - php_http_client_interface.h \ - php_http_curl_client_pool.h \ - php_http_client_pool.h \ - php_http_client_request.h \ - php_http_client_response.h \ - php_http_cookie.h \ - php_http_curl.h \ - php_http_encoding.h \ - php_http_env.h \ - php_http_env_request.h \ - php_http_env_response.h \ - php_http_etag.h \ - php_http_exception.h \ - php_http_filter.h \ - php_http.h \ - php_http_header_parser.h \ - php_http_header.h \ - php_http_info.h \ - php_http_message_body.h \ - php_http_message.h \ - php_http_message_parser.h \ - php_http_misc.h \ - php_http_negotiate.h \ - php_http_object.h \ - php_http_options.h \ - php_http_params.h \ - php_http_querystring.h \ - php_http_response_codes.h \ - php_http_url.h \ - php_http_utf8.h \ - php_http_version.h \ - " - PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS) + PHP_HTTP_HEADERS=`(cd $ext_srcdir/src && echo *.h)` + PHP_INSTALL_HEADERS(ext/http, php_http.h $PHP_HTTP_HEADERS) + PHP_SUBST([PHP_HTTP_HEADERS]) + PHP_HTTP_SRCDIR=$ext_srcdir + PHP_SUBST([PHP_HTTP_SRCDIR]) + PHP_HTTP_BUILDDIR=$ext_builddir + PHP_SUBST([PHP_HTTP_BUILDDIR]) + PHP_ADD_MAKEFILE_FRAGMENT AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support]) + if $HTTP_HAVE_A_REQUEST_LIB; then + AC_DEFINE([PHP_HTTP_HAVE_CLIENT], [1], [Have HTTP client support]) + fi fi diff --cc scripts/gen_curlinfo.php index 0000000,076a10d..2e2100f mode 000000,100755..100755 --- a/scripts/gen_curlinfo.php +++ b/scripts/gen_curlinfo.php @@@ -1,0 -1,99 +1,100 @@@ + #!/usr/bin/env php + 'PHP_HTTP_CURL_VERSION(7,19,0)', + 'APPCONNECT_TIME' => 'PHP_HTTP_CURL_VERSION(7,19,0)', + 'CONDITION_UNMET' => 'PHP_HTTP_CURL_VERSION(7,19,4)', + 'PRIMARY_PORT' => 'PHP_HTTP_CURL_VERSION(7,21,0)', + 'LOCAL_PORT' => 'PHP_HTTP_CURL_VERSION(7,21,0)', + 'LOCAL_IP' => 'PHP_HTTP_CURL_VERSION(7,21,0)', + ); + $exclude = array( + 'PRIVATE', 'LASTSOCKET', 'FTP_ENTRY_PATH', 'CERTINFO', 'TLS_SESSION', + 'RTSP_SESSION_ID', 'RTSP_CLIENT_CSEQ', 'RTSP_SERVER_CSEQ', 'RTSP_CSEQ_RECV', + 'COOKIELIST' + ); + + $translate = array( + 'HTTP_CONNECTCODE' => "connect_code", + 'COOKIELIST' => 'cookies', + ); + + $templates = array( + 'STRING' => + ' if (CURLE_OK == curl_easy_getinfo(ch, %s, &c)) { - add_assoc_string_ex(&array, "%s", sizeof("%2$s"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp); + } + ', + 'DOUBLE' => + ' if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) { - add_assoc_double_ex(&array, "%s", sizeof("%2$s"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp); + } + ', + 'LONG' => + ' if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) { - add_assoc_long_ex(&array, "%s", sizeof("%2$s"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp); + } + ', + 'SLIST' => + ' if (CURLE_OK == curl_easy_getinfo(ch, %s, &s)) { - MAKE_STD_ZVAL(subarray); - array_init(subarray); ++ array_init(&tmp); + for (p = s; p; p = p->next) { + if (p->data) { - add_next_index_string(subarray, p->data, 1); ++ add_next_index_string(&tmp, p->data); + } + } - add_assoc_zval_ex(&array, "%s", sizeof("%2$s"), subarray); ++ zend_hash_str_update(info, "%s", lenof("%2$s"), &tmp); + curl_slist_free_all(s); + } + ', + ); + + $infos = file_re('curl.h', '/^\s*(CURLINFO_(\w+))\s*=\s*CURLINFO_(STRING|LONG|DOUBLE|SLIST)\s*\+\s*\d+\s*,?\s*$/m'); + + ob_start(); + foreach ($infos as $info) { + list(, $full, $short, $type) = $info; + if (in_array($short, $exclude)) continue; + if (isset($ifdefs[$short])) printf("#if %s\n", $ifdefs[$short]); + printf($templates[$type], $full, strtolower((isset($translate[$short])) ? $translate[$short] : $short)); + if (isset($ifdefs[$short])) printf("#endif\n"); + } + + file_put_contents("php_http_client_curl.c", + preg_replace('/(\/\* BEGIN::CURLINFO \*\/\n).*(\n\s*\/\* END::CURLINFO \*\/)/s', '$1'. ob_get_contents() .'$2', + file_get_contents("php_http_client_curl.c"))); + + ?> diff --cc scripts/gen_travis_yml.php index 0000000,89ab0b2..60dbc6a mode 000000,100755..100755 --- a/scripts/gen_travis_yml.php +++ b/scripts/gen_travis_yml.php @@@ -1,0 -1,45 +1,46 @@@ + #!/usr/bin/env php + # autogenerated file; do not edit + language: c + + addons: + apt: + packages: + - php5-cli + - php-pear + - libcurl4-openssl-dev + - zlib1g-dev + - libidn11-dev + - libevent-dev + + env: + ["5.4", "5.5", "5.6"], ++ "PHP" => ["master"], + "enable_debug", + "enable_maintainer_zts", + "enable_json", + "enable_hash" => ["yes"], - "enable_iconv" => ["yes"] ++ "enable_iconv" => ["yes"], ++ "enable_phar" => ["yes"], ++ "enable_posix" => ["yes"] + ]); + foreach ($env as $e) { + printf(" - %s\n", $e); + } + + ?> + + before_script: + - make -f travis/pecl/Makefile php - - make -f travis/pecl/Makefile pecl PECL=raphf - - make -f travis/pecl/Makefile pecl PECL=propro ++ - make -f travis/pecl/Makefile pharext/raphf-phpng pharext/propro-phpng + + script: + - make -f travis/pecl/Makefile ext PECL=http + - make -f travis/pecl/Makefile test + + after_script: + - test -e tests/helper/server.log && cat tests/helper/server.log + + sudo: false diff --cc src/php_http.c index 0000000,2ff20f1..f9bb4d0 mode 000000,100644..100644 --- a/src/php_http.c +++ b/src/php_http.c @@@ -1,0 -1,252 +1,245 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + #include + + #include + + #if PHP_HTTP_HAVE_CURL + # include + # if PHP_HTTP_HAVE_EVENT + # if PHP_HTTP_HAVE_EVENT2 + # include + # include + # else + # include + # endif + # endif + #endif + #if PHP_HTTP_HAVE_IDN2 + # include + #elif PHP_HTTP_HAVE_IDN + # include + #endif + + ZEND_DECLARE_MODULE_GLOBALS(php_http); + + #ifdef COMPILE_DL_HTTP + ZEND_GET_MODULE(http) + #endif + + zend_function_entry http_functions[] = { + EMPTY_FUNCTION_ENTRY + }; + + PHP_MINIT_FUNCTION(http); + PHP_MSHUTDOWN_FUNCTION(http); + PHP_RSHUTDOWN_FUNCTION(http); + PHP_MINFO_FUNCTION(http); + + static zend_module_dep http_module_deps[] = { + ZEND_MOD_REQUIRED("raphf") + ZEND_MOD_REQUIRED("propro") + ZEND_MOD_REQUIRED("spl") + #ifdef PHP_HTTP_HAVE_HASH + ZEND_MOD_REQUIRED("hash") + #endif + #ifdef PHP_HTTP_HAVE_ICONV + ZEND_MOD_REQUIRED("iconv") + #endif + {NULL, NULL, NULL, 0} + }; + + zend_module_entry http_module_entry = { + STANDARD_MODULE_HEADER_EX, + NULL, + http_module_deps, + "http", + http_functions, + PHP_MINIT(http), + PHP_MSHUTDOWN(http), + NULL, + PHP_RSHUTDOWN(http), + PHP_MINFO(http), + PHP_PECL_HTTP_VERSION, + STANDARD_MODULE_PROPERTIES + }; + + int http_module_number; + + #if PHP_DEBUG && !HAVE_GCOV + void _dpf(int type, const char *data, size_t length) + { + static const char _sym[] = "><><><"; + if (type) { + int nwp = 0; + for (fprintf(stderr, "%c ", _sym[type-1]); length--; data++) { + int ip = PHP_HTTP_IS_CTYPE(print, *data); + if (!ip && *data != '\r' && *data != '\n') nwp = 1; + fprintf(stderr, ip?"%c":"\\x%02x", (int) (*data & 0xff)); + if (!nwp && *data == '\n' && length) { + fprintf(stderr, "\n%c ", _sym[type-1]); + } + } + fprintf(stderr, "\n"); + } else { + fprintf(stderr, "# %.*s\n", (int) length, data); + } + } + #endif + + static void php_http_globals_init_once(zend_php_http_globals *G) + { + memset(G, 0, sizeof(*G)); + } + + #if 0 -static inline void php_http_globals_init(zend_php_http_globals *G TSRMLS_DC) ++static inline void php_http_globals_init(zend_php_http_globals *G) + { + } + -static inline void php_http_globals_free(zend_php_http_globals *G TSRMLS_DC) ++static inline void php_http_globals_free(zend_php_http_globals *G) + { + } + #endif + -#if ZTS && PHP_DEBUG && !HAVE_GCOV -zend_php_http_globals *php_http_globals(void) -{ - TSRMLS_FETCH(); - return PHP_HTTP_G; -} -#endif - + PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("http.etag.mode", "crc32b", PHP_INI_ALL, OnUpdateString, env.etag_mode, zend_php_http_globals, php_http_globals) + PHP_INI_END() + + PHP_MINIT_FUNCTION(http) + { + http_module_number = module_number; + ZEND_INIT_MODULE_GLOBALS(php_http, php_http_globals_init_once, NULL); + REGISTER_INI_ENTRIES(); + + if (0 ++ || SUCCESS != PHP_MINIT_CALL(http_object) + || SUCCESS != PHP_MINIT_CALL(http_exception) + || SUCCESS != PHP_MINIT_CALL(http_cookie) + || SUCCESS != PHP_MINIT_CALL(http_encoding) + || SUCCESS != PHP_MINIT_CALL(http_filter) + || SUCCESS != PHP_MINIT_CALL(http_header) + || SUCCESS != PHP_MINIT_CALL(http_header_parser) + || SUCCESS != PHP_MINIT_CALL(http_message) + || SUCCESS != PHP_MINIT_CALL(http_message_parser) + || SUCCESS != PHP_MINIT_CALL(http_message_body) + || SUCCESS != PHP_MINIT_CALL(http_querystring) + || SUCCESS != PHP_MINIT_CALL(http_client) + || SUCCESS != PHP_MINIT_CALL(http_client_request) + || SUCCESS != PHP_MINIT_CALL(http_client_response) + #if PHP_HTTP_HAVE_CURL + || SUCCESS != PHP_MINIT_CALL(http_curl) + || SUCCESS != PHP_MINIT_CALL(http_client_curl) + #endif + || SUCCESS != PHP_MINIT_CALL(http_url) + || SUCCESS != PHP_MINIT_CALL(http_env) + || SUCCESS != PHP_MINIT_CALL(http_env_request) + || SUCCESS != PHP_MINIT_CALL(http_env_response) + || SUCCESS != PHP_MINIT_CALL(http_params) + ) { + return FAILURE; + } + + return SUCCESS; + } + + + + PHP_MSHUTDOWN_FUNCTION(http) + { + UNREGISTER_INI_ENTRIES(); + + if (0 + || SUCCESS != PHP_MSHUTDOWN_CALL(http_message) + #if PHP_HTTP_HAVE_CURL + || SUCCESS != PHP_MSHUTDOWN_CALL(http_client_curl) + || SUCCESS != PHP_MSHUTDOWN_CALL(http_curl) + #endif + || SUCCESS != PHP_MSHUTDOWN_CALL(http_client) + ) { + return FAILURE; + } + + return SUCCESS; + } + + PHP_RSHUTDOWN_FUNCTION(http) + { + if (0 + || SUCCESS != PHP_RSHUTDOWN_CALL(http_env) + ) { + return FAILURE; + } + + return SUCCESS; + } + + PHP_MINFO_FUNCTION(http) + { + php_http_buffer_t buf; + + php_http_buffer_init(&buf); + + php_info_print_table_start(); + php_info_print_table_header(2, "HTTP Support", "enabled"); + php_info_print_table_row(2, "Extension Version", PHP_PECL_HTTP_VERSION); + php_info_print_table_end(); + + php_info_print_table_start(); + php_info_print_table_header(3, "Used Library", "Compiled", "Linked"); + php_info_print_table_row(3, "libz", ZLIB_VERSION, zlibVersion()); + #if PHP_HTTP_HAVE_CURL + { + curl_version_info_data *cv = curl_version_info(CURLVERSION_NOW); + php_info_print_table_row(3, "libcurl", LIBCURL_VERSION, cv->version); + } + #else + php_info_print_table_row(3, "libcurl", "disabled", "disabled"); + #endif + + #if PHP_HTTP_HAVE_EVENT + php_info_print_table_row(3, "libevent", + # ifdef LIBEVENT_VERSION + LIBEVENT_VERSION, + # else + PHP_HTTP_EVENT_VERSION, + # endif + event_get_version()); + #else + php_info_print_table_row(3, "libevent", "disabled", "disabled"); + #endif + + #if PHP_HTTP_HAVE_IDN2 + php_info_print_table_row(3, "libidn2 (IDNA2008)", IDN2_VERSION, idn2_check_version(NULL)); + #elif PHP_HTTP_HAVE_IDN + php_info_print_table_row(3, "libidn (IDNA2003)", PHP_HTTP_LIBIDN_VERSION, "unknown"); + #endif + + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); + } + + + /* + * 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 --cc src/php_http_api.h index 0000000,08b6ba8..2313413 mode 000000,100644..100644 --- a/src/php_http_api.h +++ b/src/php_http_api.h @@@ -1,0 -1,141 +1,148 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_API_H + #define PHP_HTTP_API_H + + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #ifndef PHP_WIN32 + #include + #endif + #include + #include + + #include + #include + #include + #include + #include + + #include + #include + + + #ifdef PHP_WIN32 + # define PHP_HTTP_API __declspec(dllexport) + #elif defined(__GNUC__) && __GNUC__ >= 4 + # define PHP_HTTP_API extern __attribute__ ((visibility("default"))) + #else + # define PHP_HTTP_API extern + #endif + + #if (defined(HAVE_ICONV) || defined(PHP_HTTP_HAVE_EXT_ICONV)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_ICONV)) + # define PHP_HTTP_HAVE_ICONV + #endif + + #if (defined(HAVE_HASH_EXT) || defined(PHP_HTTP_HAVE_EXT_HASH)) && (PHP_HTTP_SHARED_DEPS || !defined(COMPILE_DL_HASH)) && defined(PHP_HTTP_HAVE_PHP_HASH_H) + # define PHP_HTTP_HAVE_HASH + #endif + + #include + + #ifdef PHP_WIN32 + # define CURL_STATICLIB + # include + #else + # ifdef HAVE_NETDB_H + # include + # endif + # ifdef HAVE_UNISTD_H + # include + # endif + #endif + + #if defined(HAVE_WCHAR_H) && defined(HAVE_WCTYPE_H) && defined(HAVE_ISWALNUM) && (defined(HAVE_MBRTOWC) || defined(HAVE_MBTOWC)) + # define PHP_HTTP_HAVE_WCHAR 1 + #endif + + #include + #define PHP_HTTP_IS_CTYPE(type, c) is##type((int) (unsigned char) (c)) + #define PHP_HTTP_TO_CTYPE(type, c) to##type((int) (unsigned char) (c)) + + #include "php_http.h" + + #include "php_http_buffer.h" + #include "php_http_misc.h" + #include "php_http_options.h" + + #include "php_http.h" + #include "php_http_cookie.h" + #include "php_http_encoding.h" + #include "php_http_info.h" + #include "php_http_message.h" + #include "php_http_env.h" + #include "php_http_env_request.h" + #include "php_http_env_response.h" + #include "php_http_etag.h" + #include "php_http_exception.h" + #include "php_http_filter.h" + #include "php_http_header_parser.h" + #include "php_http_header.h" + #include "php_http_message_body.h" + #include "php_http_message_parser.h" + #include "php_http_negotiate.h" + #include "php_http_object.h" + #include "php_http_params.h" + #include "php_http_querystring.h" + #include "php_http_client.h" + #include "php_http_curl.h" + #include "php_http_client_request.h" + #include "php_http_client_response.h" + #include "php_http_client_curl.h" + #include "php_http_url.h" + #include "php_http_version.h" + + ZEND_BEGIN_MODULE_GLOBALS(php_http) + struct php_http_env_globals env; ++#ifdef PHP_HTTP_HAVE_CLIENT ++ struct { ++#ifdef PHP_HTTP_HAVE_CURL ++ struct php_http_client_curl_globals curl; ++#endif ++ } client; ++#endif + ZEND_END_MODULE_GLOBALS(php_http) + + ZEND_EXTERN_MODULE_GLOBALS(php_http); + + #ifdef ZTS + # include "TSRM/TSRM.h" -# define PHP_HTTP_G ((zend_php_http_globals *) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(php_http_globals_id)]) ++# define PHP_HTTP_G ((zend_php_http_globals *) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(php_http_globals_id)]) + # undef TSRMLS_FETCH_FROM_CTX -# define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = ((ctx)?(ctx):ts_resource_ex(0, NULL)) ++# define TSRMLS_FETCH_FROM_CTX(ctx) ERROR + #else + # define PHP_HTTP_G (&php_http_globals) + #endif + + #if PHP_DEBUG + # define _DPF_STR 0 + # define _DPF_IN 1 + # define _DPF_OUT 2 + extern void _dpf(int type, const char *data, size_t length); + #else + # define _dpf(t,s,l); + #endif + + #endif /* PHP_HTTP_API_H */ + + + /* + * 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 --cc src/php_http_buffer.c index 0000000,e24a4e1..b84bcd0 mode 000000,100644..100644 --- a/src/php_http_buffer.c +++ b/src/php_http_buffer.c @@@ -1,0 -1,459 +1,502 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include + #include "php_http_buffer.h" + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex( ++ php_http_buffer_t *buf, size_t chunk_size, unsigned flags) + { + if (!buf) { + buf = pemalloc(sizeof(*buf), flags & PHP_HTTP_BUFFER_INIT_PERSISTENT); + } + + if (buf) { + buf->size = (chunk_size) ? chunk_size : PHP_HTTP_BUFFER_DEFAULT_SIZE; + buf->pmem = (flags & PHP_HTTP_BUFFER_INIT_PERSISTENT) ? 1 : 0; - buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? pemalloc(buf->size, buf->pmem) : NULL; ++ buf->data = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? ++ pemalloc(buf->size, buf->pmem) : NULL; + buf->free = (flags & PHP_HTTP_BUFFER_INIT_PREALLOC) ? buf->size : 0; + buf->used = 0; + } + + return buf; + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex( ++ php_http_buffer_t *buf, const char *str, size_t len) + { + if ((buf = php_http_buffer_init(buf))) { - if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) { ++ if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, str, len)) { + pefree(buf, buf->pmem); + buf = NULL; + } + } + return buf; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex( ++ php_http_buffer_t *buf, size_t len, size_t override_size, ++ zend_bool allow_error) + { + char *ptr = NULL; + #if 0 + fprintf(stderr, "RESIZE: len=%lu, size=%lu, used=%lu, free=%lu, total=%lu\n", len, buf->size, buf->used, buf->free, buf->free+buf->used); + #endif + if (buf->free < len) { + size_t size = override_size ? override_size : buf->size; + + while ((size + buf->free) < len) { + size <<= 1; + } + + if (allow_error) { - ptr = perealloc_recoverable(buf->data, buf->used + buf->free + size, buf->pmem); ++ ptr = perealloc_recoverable(buf->data, ++ buf->used + buf->free + size, buf->pmem); + } else { + ptr = perealloc(buf->data, buf->used + buf->free + size, buf->pmem); + } + + if (ptr) { + buf->data = ptr; + } else { + return PHP_HTTP_BUFFER_NOMEM; + } + + buf->free += size; + return size; + } + return 0; + } + -PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account) ++PHP_HTTP_BUFFER_API char *php_http_buffer_account( ++ php_http_buffer_t *buf, size_t to_account) + { + assert(to_account <= buf->free); + + buf->free -= to_account; + buf->used += to_account; + + return buf->data + buf->used; + } + + PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf) + { + /* avoid another realloc on fixation */ + if (buf->free > 1) { + char *ptr = perealloc(buf->data, buf->used + 1, buf->pmem); + + if (ptr) { + buf->data = ptr; + } else { + return PHP_HTTP_BUFFER_NOMEM; + } + buf->free = 1; + } + return buf->used; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, ++ const char *append, size_t append_len) + { - if (buf->free < append_len && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len)) { ++ if ( buf->free < append_len && ++ PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, append_len) ++ ) { + return PHP_HTTP_BUFFER_NOMEM; + } + memcpy(buf->data + buf->used, append, append_len); + buf->used += append_len; + buf->free -= append_len; + return append_len; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, ++ const char *format, ...) + { + va_list argv; + char *append; + size_t append_len, alloc; + + va_start(argv, format); + append_len = vspprintf(&append, 0, format, argv); + va_end(argv); + + alloc = php_http_buffer_append(buf, append, append_len); + efree(append); + + if (PHP_HTTP_BUFFER_NOMEM == alloc) { + return PHP_HTTP_BUFFER_NOMEM; + } + return append_len; + } + -PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len) ++PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, ++ char **into, size_t *len) + { + char *copy = ecalloc(1, buf->used + 1); + memcpy(copy, buf->data, buf->used); + if (into) { + *into = copy; + } + if (len) { + *len = buf->used; + } + return copy; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, ++ size_t offset, size_t length) + { + if (offset > buf->used) { + return 0; + } + if (offset + length > buf->used) { + length = buf->used - offset; + } - memmove(buf->data + offset, buf->data + offset + length, buf->used - length - offset); ++ memmove(buf->data + offset, buf->data + offset + length, ++ buf->used - length - offset); + buf->used -= length; + buf->free += length; + return length; + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix( ++ php_http_buffer_t *buf) + { - if (buf->free < 1 && PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0)) { ++ if ( buf->free < 1 && ++ PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(buf, 1, 1, 0) ++ ) { + return NULL; + } + buf->data[buf->used] = '\0'; + return buf; + } + + PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf) + { + buf->free += buf->used; + buf->used = 0; + } + + PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf) + { + if (buf->data) { + pefree(buf->data, buf->pmem); + buf->data = NULL; + } + buf->used = 0; + buf->free = 0; + } + + PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf) + { + if (*buf) { + php_http_buffer_dtor(*buf); + pefree(*buf, (*buf)->pmem); + *buf = NULL; + } + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, ++ const char *data, size_t data_len, char **chunk, size_t chunk_size) + { + php_http_buffer_t *storage; + + *chunk = NULL; + + if (!*s) { - *s = php_http_buffer_init_ex(NULL, chunk_size << 1, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0); ++ *s = php_http_buffer_init_ex(NULL, chunk_size << 1, ++ chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0); + } + storage = *s; + + if (data_len) { + php_http_buffer_append(storage, data, data_len); + } + + if (!chunk_size) { + php_http_buffer_data(storage, chunk, &chunk_size); + php_http_buffer_free(s); + return chunk_size; + } + + if (storage->used >= chunk_size) { + *chunk = estrndup(storage->data, chunk_size); + php_http_buffer_cut(storage, 0, chunk_size); + return chunk_size; + } + + return 0; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_len, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, ++ const char *data, size_t data_len, size_t chunk_len, ++ php_http_buffer_pass_func_t passout, void *opaque) + { + char *chunk = NULL; + size_t passed = 0, got = 0; + + while ((got = php_http_buffer_chunk_buffer(s, data, data_len, &chunk, chunk_len))) { - if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got TSRMLS_CC)) { ++ if (PHP_HTTP_BUFFER_PASS0 == passout(opaque, chunk, got)) { + PTR_SET(chunk, NULL); + return PHP_HTTP_BUFFER_PASS0; + } + ++passed; + if (!chunk_len) { + /* we already got the last chunk, + and freed all resources */ + break; + } + data = NULL; + data_len = 0; + PTR_SET(chunk, NULL); + } + PTR_FREE(chunk); + return passed; + } + -PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC) ++PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, ++ php_http_buffer_pass_func_t passin, void *passin_arg, ++ php_http_buffer_pass_func_t passon, void *passon_arg) + { - size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC); ++ size_t passed_on = 0, passed_in; ++ ++ passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg); + + if (passed_in == PHP_HTTP_BUFFER_PASS0) { + return passed_in; + } + if (passed_in || (*s)->used) { - passed_on = passon(passon_arg, (*s)->data, (*s)->used TSRMLS_CC); ++ passed_on = passon(passon_arg, (*s)->data, (*s)->used); + + if (passed_on == PHP_HTTP_BUFFER_PASS0) { + return passed_on; + } + + if (passed_on) { + php_http_buffer_cut(*s, 0, passed_on); + } + } + + return passed_on - passed_in; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, ++ size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque) + { + php_http_buffer_t *str; + size_t passed; + + if (!*s) { - *s = php_http_buffer_init_ex(NULL, chunk_size, chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0); ++ *s = php_http_buffer_init_ex(NULL, chunk_size, ++ chunk_size ? PHP_HTTP_BUFFER_INIT_PREALLOC : 0); + } + str = *s; + + php_http_buffer_resize(str, chunk_size); - passed = passin(opaque, str->data + str->used, chunk_size TSRMLS_CC); ++ passed = passin(opaque, str->data + str->used, chunk_size); + + if (passed != PHP_HTTP_BUFFER_PASS0) { + str->used += passed; + str->free -= passed; + } + + php_http_buffer_fix(str); + + return passed; + } + + #ifdef PHP_HTTP_BUFFER_EXTENDED + -PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right) ++PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, ++ php_http_buffer_t *right) + { + if (left->used > right->used) { + return -1; + } else if (right->used > left->used) { + return 1; + } else { + return memcmp(left->data, right->data, left->used); + } + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy( ++ const php_http_buffer_t *from, php_http_buffer_t *to) + { + int free_to = !to; + + to = php_http_buffer_clone(from, to); + + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(to, from->data, from->used)) { + if (free_to) { + php_http_buffer_free(&to); + } else { + php_http_buffer_dtor(to); + } + } + return to; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, ++ const char *insert, size_t insert_len, size_t offset) + { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, insert_len)) { + return PHP_HTTP_BUFFER_NOMEM; + } + memmove(buf->data + offset + insert_len, buf->data + offset, insert_len); + memcpy(buf->data + offset, insert, insert_len); + buf->used += insert_len; + buf->free -= insert_len; + return insert_len; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, ++ size_t offset, const char *format, ...) + { + va_list argv; + char *insert; + size_t insert_len, alloc; + + va_start(argv, format); + insert_len = vspprintf(&insert, 0, format, argv); + va_end(argv); + + alloc = php_http_buffer_insert(buf, insert, insert_len, offset); + efree(insert); + + if (PHP_HTTP_BUFFER_NOMEM == alloc) { + return PHP_HTTP_BUFFER_NOMEM; + } + return insert_len; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, ++ const char *prepend, size_t prepend_len) + { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize(buf, prepend_len)) { + return PHP_HTTP_BUFFER_NOMEM; + } + memmove(buf->data + prepend_len, buf->data, buf->used); + memcpy(buf->data, prepend, prepend_len); + buf->used += prepend_len; + buf->free -= prepend_len; + return prepend_len; + } + -PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) ++PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, ++ const char *format, ...) + { + va_list argv; + char *prepend; + size_t prepend_len, alloc; + + va_start(argv, format); + prepend_len = vspprintf(&prepend, 0, format, argv); + va_end(argv); + + alloc = php_http_buffer_prepend(buf, prepend, prepend_len); + efree(prepend); + + if (PHP_HTTP_BUFFER_NOMEM == alloc) { + return PHP_HTTP_BUFFER_NOMEM; + } + return prepend_len; + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t length) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub( ++ const php_http_buffer_t *buf, size_t offset, size_t length) + { + if (offset >= buf->used) { + return NULL; + } else { - size_t need = 1 + ((length + offset) > buf->used ? (buf->used - offset) : (length - offset)); - php_http_buffer_t *sub = php_http_buffer_init_ex(NULL, need, PHP_HTTP_BUFFER_INIT_PREALLOC | (buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0)); ++ php_http_buffer_t *sub; ++ size_t need = 1 + ((length + offset) > buf->used ? ++ (buf->used - offset) : (length - offset)); ++ unsigned flags = buf->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0; ++ ++ sub = php_http_buffer_init_ex(NULL, need, ++ PHP_HTTP_BUFFER_INIT_PREALLOC | flags); ++ + if (sub) { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(sub, buf->data + offset, need)) { + php_http_buffer_free(&sub); + } else { + sub->size = buf->size; + } + } + return sub; + } + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right( ++ const php_http_buffer_t *buf, size_t length) + { + if (length < buf->used) { + return php_http_buffer_sub(buf, buf->used - length, length); + } else { + return php_http_buffer_sub(buf, 0, buf->used); + } + } + + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va( ++ php_http_buffer_t *buf, unsigned argc, va_list argv) + { + unsigned i = 0; + buf = php_http_buffer_init(buf); + + if (buf) { + while (argc > i++) { + php_http_buffer_free_t f = va_arg(argv, php_http_buffer_free_t); + php_http_buffer_t *current = va_arg(argv, php_http_buffer_t *); + php_http_buffer_append(buf, current->data, current->used); + FREE_PHP_HTTP_BUFFER(f, current); + } + } + + return buf; + } + -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...) ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex( ++ php_http_buffer_t *buf, unsigned argc, ...) + { + va_list argv; + php_http_buffer_t *ret; + + va_start(argv, argc); + ret = php_http_buffer_merge_va(buf, argc, argv); + va_end(argv); + return ret; + } + + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...) + { + va_list argv; + php_http_buffer_t *ret; + + va_start(argv, argc); + ret = php_http_buffer_merge_va(NULL, argc, argv); + va_end(argv); + return ret; + } + + #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 --cc src/php_http_buffer.h index 0000000,faf8992..818c443 mode 000000,100644..100644 --- a/src/php_http_buffer.h +++ b/src/php_http_buffer.h @@@ -1,0 -1,250 +1,245 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_BUFFER_H + #define PHP_HTTP_BUFFER_H + + #ifndef PHP_HTTP_BUFFER_DEFAULT_SIZE + # define PHP_HTTP_BUFFER_DEFAULT_SIZE 256 + #endif + + #define PHP_HTTP_BUFFER_ERROR ((size_t) -1) + #define PHP_HTTP_BUFFER_NOMEM PHP_HTTP_BUFFER_ERROR + #define PHP_HTTP_BUFFER_PASS0 PHP_HTTP_BUFFER_ERROR + + #ifndef PTR_FREE + # define PTR_FREE(PTR) \ + { \ + if (PTR) { \ + efree(PTR); \ + } \ + } + #endif + #ifndef PTR_SET + # define PTR_SET(PTR, SET) \ + { \ + PTR_FREE(PTR); \ + PTR = SET; \ + } + #endif -#ifndef TSRMLS_D -# define TSRMLS_D -# define TSRMLS_DC -# define TSRMLS_CC -# define TSRMLS_C -#endif + #ifdef PHP_ATTRIBUTE_FORMAT + # define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b) PHP_ATTRIBUTE_FORMAT(f, a, b) + #else + # define PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(f, a, b) + #endif + #ifndef pemalloc + # define pemalloc(s,p) malloc(s) + # define pefree(x,p) free(x) + # define perealloc(x,s,p) realloc(x,s) + # define perealloc_recoverable perealloc + # define ecalloc calloc + static inline void *estrndup(void *p, size_t s) + { + char *r = (char *) malloc(s+1); + if (r) memcpy((void *) r, p, s), r[s] = '\0'; + return (void *) r; + } + #endif + + #if defined(PHP_WIN32) + # if defined(PHP_HTTP_BUFFER_EXPORTS) + # define PHP_HTTP_BUFFER_API __declspec(dllexport) + # elif defined(COMPILE_DL_PHP_HTTP_BUFFER) + # define PHP_HTTP_BUFFER_API __declspec(dllimport) + # else + # define PHP_HTTP_BUFFER_API + # endif + #else + # define PHP_HTTP_BUFFER_API + #endif + + #define PHP_HTTP_BUFFER(p) ((php_http_buffer_t *) (p)) + + #define FREE_PHP_HTTP_BUFFER_PTR(STR) pefree(STR, STR->pmem) + #define FREE_PHP_HTTP_BUFFER_VAL(STR) php_http_buffer_dtor(STR) + #define FREE_PHP_HTTP_BUFFER_ALL(STR) php_http_buffer_free(&(STR)) + #define FREE_PHP_HTTP_BUFFER(free, STR) \ + switch (free) \ + { \ + case PHP_HTTP_BUFFER_FREE_NOT: \ + break; \ + case PHP_HTTP_BUFFER_FREE_PTR: \ - pefree(STR, STR->pmem); break; \ ++ pefree(STR, STR->pmem); \ + break; \ + case PHP_HTTP_BUFFER_FREE_VAL: \ + php_http_buffer_dtor(STR); \ + break; \ + case PHP_HTTP_BUFFER_FREE_ALL: { \ + php_http_buffer_t *PTR = (STR); \ + php_http_buffer_free(&PTR); \ + break; \ + } \ + default:\ + break; \ + } + + #define RETURN_PHP_HTTP_BUFFER_PTR(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0) + #define RETURN_PHP_HTTP_BUFFER_VAL(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0) + #define RETURN_PHP_HTTP_BUFFER_DUP(STR) RETURN_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1) + #define RETVAL_PHP_HTTP_BUFFER_PTR(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_PTR, 0) + #define RETVAL_PHP_HTTP_BUFFER_VAL(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 0) + #define RETVAL_PHP_HTTP_BUFFER_DUP(STR) RETVAL_PHP_HTTP_BUFFER((STR), PHP_HTTP_BUFFER_FREE_NOT, 1) + /* RETURN_PHP_HTTP_BUFFER(buf, PHP_HTTP_BUFFER_FREE_PTR, 0) */ + #define RETURN_PHP_HTTP_BUFFER(STR, free, dup) \ + RETVAL_PHP_HTTP_BUFFER((STR), (free), (dup)); \ + return; + + #define RETVAL_PHP_HTTP_BUFFER(STR, free, dup) \ + php_http_buffer_fix(STR); \ + RETVAL_STRINGL((STR)->data, (STR)->used, (dup)); \ + FREE_PHP_HTTP_BUFFER((free), (STR)); + + typedef struct php_http_buffer { + char *data; + size_t used; + size_t free; + size_t size; + unsigned pmem:1; + unsigned reserved:31; + } php_http_buffer_t; + + typedef enum php_http_buffer_free { + PHP_HTTP_BUFFER_FREE_NOT = 0, + PHP_HTTP_BUFFER_FREE_PTR, /* pefree() */ + PHP_HTTP_BUFFER_FREE_VAL, /* php_http_buffer_dtor() */ + PHP_HTTP_BUFFER_FREE_ALL /* php_http_buffer_free() */ + } php_http_buffer_free_t; + + #define PHP_HTTP_BUFFER_ALL_FREE(STR) PHP_HTTP_BUFFER_FREE_ALL,(STR) + #define PHP_HTTP_BUFFER_PTR_FREE(STR) PHP_HTTP_BUFFER_FREE_PTR,(STR) + #define PHP_HTTP_BUFFER_VAL_FREE(STR) PHP_HTTP_BUFFER_FREE_VAL,(STR) + #define PHP_HTTP_BUFFER_NOT_FREE(STR) PHP_HTTP_BUFFER_FREE_NOT,(STR) + + #define PHP_HTTP_BUFFER_INIT_PREALLOC 0x01 + #define PHP_HTTP_BUFFER_INIT_PERSISTENT 0x02 + + /* create a new php_http_buffer_t */ + #define php_http_buffer_new() php_http_buffer_init(NULL) + #define php_http_buffer_init(b) php_http_buffer_init_ex(b, PHP_HTTP_BUFFER_DEFAULT_SIZE, 0) + #define php_http_buffer_clone(from, to) php_http_buffer_init_ex((to), (from)->size, (from)->pmem ? PHP_HTTP_BUFFER_INIT_PERSISTENT:0) -PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, int flags); ++PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_init_ex(php_http_buffer_t *buf, size_t chunk_size, unsigned flags); + + /* create a php_http_buffer_t from a zval or c-string */ + #define php_http_buffer_from_zval(z) php_http_buffer_from_string(Z_STRVAL(z), Z_STRLEN(z)) + #define php_http_buffer_from_zval_ex(b, z) php_http_buffer_from_string_ex(b, Z_STRVAL(z), Z_STRLEN(z)) + #define php_http_buffer_from_string(s, l) php_http_buffer_from_string_ex(NULL, (s), (l)) + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_from_string_ex(php_http_buffer_t *buf, const char *string, size_t length); + + /* usually only called from within the internal functions */ + #define php_http_buffer_resize(b, s) php_http_buffer_resize_ex((b), (s), 0, 0) -PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, int allow_error); ++PHP_HTTP_BUFFER_API size_t php_http_buffer_resize_ex(php_http_buffer_t *buf, size_t len, size_t override_size, zend_bool allow_error); + + PHP_HTTP_BUFFER_API char *php_http_buffer_account(php_http_buffer_t *buf, size_t to_account); + + /* shrink memory chunk to actually used size (+1) */ + PHP_HTTP_BUFFER_API size_t php_http_buffer_shrink(php_http_buffer_t *buf); + + /* append data to the php_http_buffer_t */ + #define php_http_buffer_appends(b, a) php_http_buffer_append((b), (a), sizeof(a)-1) + #define php_http_buffer_appendl(b, a) php_http_buffer_append((b), (a), strlen(a)) ++#define php_http_buffer_appendz(b, z) php_http_buffer_append((b), (z)->val, (z)->len) + PHP_HTTP_BUFFER_API size_t php_http_buffer_append(php_http_buffer_t *buf, const char *append, size_t append_len); + PHP_HTTP_BUFFER_API size_t php_http_buffer_appendf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3); + + /* get a zero-terminated string */ + PHP_HTTP_BUFFER_API char *php_http_buffer_data(const php_http_buffer_t *buf, char **into, size_t *len); + + /* remove a substring */ + PHP_HTTP_BUFFER_API size_t php_http_buffer_cut(php_http_buffer_t *buf, size_t offset, size_t length); + + /* sets a trailing NUL byte */ + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_fix(php_http_buffer_t *buf); + + /* reset php_http_buffer_t object */ + PHP_HTTP_BUFFER_API void php_http_buffer_reset(php_http_buffer_t *buf); + + /* free a php_http_buffer_t objects contents */ + PHP_HTTP_BUFFER_API void php_http_buffer_dtor(php_http_buffer_t *buf); + + /* free a php_http_buffer_t object completely */ + PHP_HTTP_BUFFER_API void php_http_buffer_free(php_http_buffer_t **buf); + + /* stores data in a php_http_buffer_t until it reaches chunk_size */ + PHP_HTTP_BUFFER_API size_t php_http_buffer_chunk_buffer(php_http_buffer_t **s, const char *data, size_t data_len, char **chunk, size_t chunk_size); + -typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t TSRMLS_DC); ++typedef size_t (*php_http_buffer_pass_func_t)(void *opaque, char *, size_t); + -PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg TSRMLS_DC); ++PHP_HTTP_BUFFER_API ssize_t php_http_buffer_passthru(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *passin_arg, php_http_buffer_pass_func_t passon, void *passon_arg); + + /* wrapper around php_http_buffer_chunk_buffer, which passes available chunks to passthru() */ -PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque TSRMLS_DC); ++PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_output(php_http_buffer_t **s, const char *data, size_t data_len, size_t chunk_size, php_http_buffer_pass_func_t passout, void *opaque); + + /* write chunks directly into php_http_buffer_t buffer */ -PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque TSRMLS_DC); ++PHP_HTTP_BUFFER_API size_t php_http_buffer_chunked_input(php_http_buffer_t **s, size_t chunk_size, php_http_buffer_pass_func_t passin, void *opaque); + + + # ifdef PHP_HTTP_BUFFER_EXTENDED + + /* memcmp for php_http_buffer_t objects */ + PHP_HTTP_BUFFER_API int php_http_buffer_cmp(php_http_buffer_t *left, php_http_buffer_t *right); + + /* get a complete php_http_buffer_t duplicate */ + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_copy(const php_http_buffer_t *from, php_http_buffer_t *to); + + /* merge several php_http_buffer_t objects + use like: + + php_http_buffer_t *final = php_http_buffer_merge(3, + PHP_HTTP_BUFFER_NOT_FREE(&keep), + PHP_HTTP_BUFFER_ALL_FREE(middle_ptr), + PHP_HTTP_BUFFER_VAL_FREE(&local); + */ + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge(unsigned argc, ...); + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_ex(php_http_buffer_t *buf, unsigned argc, ...); + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_merge_va(php_http_buffer_t *buf, unsigned argc, va_list argv); + + /* insert data at a specific position of the php_http_buffer_t */ + #define php_http_buffer_inserts(b, i, o) php_http_buffer_insert((b), (i), sizeof(i)-1, (o)) + #define php_http_buffer_insertl(b, i, o) php_http_buffer_insert((b), (i), strlen(i), (o)) + PHP_HTTP_BUFFER_API size_t php_http_buffer_insert(php_http_buffer_t *buf, const char *insert, size_t insert_len, size_t offset); + PHP_HTTP_BUFFER_API size_t php_http_buffer_insertf(php_http_buffer_t *buf, size_t offset, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 3, 4); + + /* prepend data */ + #define php_http_buffer_prepends(b, p) php_http_buffer_prepend((b), (p), sizeof(p)-1) + #define php_http_buffer_prependl(b, p) php_http_buffer_prepend((b), (p), strlen(p)) + PHP_HTTP_BUFFER_API size_t php_http_buffer_prepend(php_http_buffer_t *buf, const char *prepend, size_t prepend_len); + PHP_HTTP_BUFFER_API size_t php_http_buffer_prependf(php_http_buffer_t *buf, const char *format, ...) PHP_HTTP_BUFFER_ATTRIBUTE_FORMAT(printf, 2, 3); + + /* get a part of the php_http_buffer_t */ + #define php_http_buffer_mid(b, o, l) php_http_buffer_sub((b), (o), (l)) + #define php_http_buffer_left(b, l) php_http_buffer_sub((b), 0, (l)) + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_right(const php_http_buffer_t *buf, size_t length); + PHP_HTTP_BUFFER_API php_http_buffer_t *php_http_buffer_sub(const php_http_buffer_t *buf, size_t offset, size_t len); + + # endif /* PHP_HTTP_BUFFER_EXTENDED */ + + #endif /* PHP_HTTP_BUFFER_H */ + + + /* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --cc src/php_http_client.c index 0000000,160e8bb..5d78803 mode 000000,100644..100644 --- a/src/php_http_client.c +++ b/src/php_http_client.c @@@ -1,0 -1,1284 +1,1252 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + #include "php_http_client.h" + + #include + + /* + * array of name => php_http_client_driver_t* + */ + static HashTable php_http_client_drivers; + ++static void php_http_client_driver_hash_dtor(zval *pData) ++{ ++ pefree(Z_PTR_P(pData), 1); ++} ++ + ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver) + { - return zend_hash_add(&php_http_client_drivers, driver->name_str, driver->name_len + 1, (void *) driver, sizeof(php_http_client_driver_t), NULL); ++ return zend_hash_add_mem(&php_http_client_drivers, driver->driver_name, (void *) driver, sizeof(php_http_client_driver_t)) ++ ? SUCCESS : FAILURE; + } + -ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver) ++php_http_client_driver_t *php_http_client_driver_get(zend_string *name) + { ++ zval *ztmp; + php_http_client_driver_t *tmp; + - if ((name_str && SUCCESS == zend_hash_find(&php_http_client_drivers, name_str, name_len + 1, (void *) &tmp)) - || (SUCCESS == zend_hash_get_current_data(&php_http_client_drivers, (void *) &tmp))) { - *driver = *tmp; - return SUCCESS; ++ if (name && (tmp = zend_hash_find_ptr(&php_http_client_drivers, name))) { ++ return tmp; + } - return FAILURE; ++ if ((ztmp = zend_hash_get_current_data(&php_http_client_drivers))) { ++ return Z_PTR_P(ztmp); ++ } ++ return NULL; + } + -static int apply_driver_list(void *p, void *arg TSRMLS_DC) ++static int apply_driver_list(zval *p, void *arg) + { - php_http_client_driver_t *d = p; - zval *zname; ++ php_http_client_driver_t *d = Z_PTR_P(p); ++ zval zname; + - MAKE_STD_ZVAL(zname); - ZVAL_STRINGL(zname, d->name_str, d->name_len, 1); ++ ZVAL_STR_COPY(&zname, d->driver_name); + - zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL); ++ zend_hash_next_index_insert(arg, &zname); + return ZEND_HASH_APPLY_KEEP; + } + -void php_http_client_driver_list(HashTable *ht TSRMLS_DC) ++void php_http_client_driver_list(HashTable *ht) + { - zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC); ++ zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht); + } + -void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC) ++void php_http_client_options_set_subr(zval *instance, char *key, size_t len, zval *opts, int overwrite) + { + if (overwrite || (opts && zend_hash_num_elements(Z_ARRVAL_P(opts)))) { - zend_class_entry *this_ce = Z_OBJCE_P(getThis()); - zval *old_opts, *new_opts, **entry = NULL; ++ zend_class_entry *this_ce = Z_OBJCE_P(instance); ++ zval old_opts_tmp, *old_opts, new_opts, *entry = NULL; + - MAKE_STD_ZVAL(new_opts); - array_init(new_opts); - old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ array_init(&new_opts); ++ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { - array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts)); ++ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL(new_opts)); + } + + if (overwrite) { + if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) { + Z_ADDREF_P(opts); - zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL); ++ zend_symtable_str_update(Z_ARRVAL(new_opts), key, len, opts); + } else { - zend_symtable_del(Z_ARRVAL_P(new_opts), key, len); ++ zend_symtable_str_del(Z_ARRVAL(new_opts), key, len); + } + } else if (opts && zend_hash_num_elements(Z_ARRVAL_P(opts))) { - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(new_opts), key, len, (void *) &entry)) { - array_join(Z_ARRVAL_P(opts), Z_ARRVAL_PP(entry), 0, 0); ++ if ((entry = zend_symtable_str_find(Z_ARRVAL(new_opts), key, len))) { ++ array_join(Z_ARRVAL_P(opts), Z_ARRVAL_P(entry), 0, 0); + } else { + Z_ADDREF_P(opts); - zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL); ++ zend_symtable_str_update(Z_ARRVAL(new_opts), key, len, opts); + } + } + - zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC); ++ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts); + zval_ptr_dtor(&new_opts); + } + } + -void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC) ++void php_http_client_options_set(zval *instance, zval *opts) + { - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - HashPosition pos; - zval *new_opts; - zend_class_entry *this_ce = Z_OBJCE_P(getThis()); - zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry TSRMLS_CC); ++ php_http_arrkey_t key; ++ zval new_opts; ++ zend_class_entry *this_ce = Z_OBJCE_P(instance); ++ zend_bool is_client = instanceof_function(this_ce, php_http_client_class_entry); + - MAKE_STD_ZVAL(new_opts); - array_init(new_opts); ++ array_init(&new_opts); + + if (!opts || !zend_hash_num_elements(Z_ARRVAL_P(opts))) { - zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC); ++ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts); + zval_ptr_dtor(&new_opts); + } else { - zval *old_opts, *add_opts, **opt; ++ zval old_opts_tmp, *old_opts, add_opts, *opt; + - MAKE_STD_ZVAL(add_opts); - array_init(add_opts); ++ array_init(&add_opts); + /* some options need extra attention -- thus cannot use array_merge() directly */ - FOREACH_KEYVAL(pos, opts, key, opt) { - if (key.type == HASH_KEY_IS_STRING) { -#define KEYMATCH(k, s) ((sizeof(s)==k.len) && !strcasecmp(k.str, s)) - if (Z_TYPE_PP(opt) == IS_ARRAY && (KEYMATCH(key, "ssl") || KEYMATCH(key, "cookies"))) { - php_http_client_options_set_subr(getThis(), key.str, key.len, *opt, 0 TSRMLS_CC); - } else if (is_client && (KEYMATCH(key, "recordHistory") || KEYMATCH(key, "responseMessageClass"))) { - zend_update_property(this_ce, getThis(), key.str, key.len-1, *opt TSRMLS_CC); - } else if (Z_TYPE_PP(opt) == IS_NULL) { - old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(opts), key.h, key.key, opt) ++ { ++ if (key.key) { ++ if (Z_TYPE_P(opt) == IS_ARRAY && (zend_string_equals_literal(key.key, "ssl") || zend_string_equals_literal(key.key, "cookies"))) { ++ php_http_client_options_set_subr(instance, key.key->val, key.key->len, opt, 0); ++ } else if (is_client && (zend_string_equals_literal(key.key, "recordHistory") || zend_string_equals_literal(key.key, "responseMessageClass"))) { ++ zend_update_property(this_ce, instance, key.key->val, key.key->len, opt); ++ } else if (Z_TYPE_P(opt) == IS_NULL) { ++ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { - zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len); ++ zend_symtable_del(Z_ARRVAL_P(old_opts), key.key); + } + } else { - Z_ADDREF_P(*opt); - add_assoc_zval_ex(add_opts, key.str, key.len, *opt); ++ Z_TRY_ADDREF_P(opt); ++ add_assoc_zval_ex(&add_opts, key.key->val, key.key->len, opt); + } + } + } ++ ZEND_HASH_FOREACH_END(); + - old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ old_opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &old_opts_tmp); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { - array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(new_opts)); ++ array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL(new_opts)); + } - array_join(Z_ARRVAL_P(add_opts), Z_ARRVAL_P(new_opts), 0, 0); - zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC); ++ array_join(Z_ARRVAL(add_opts), Z_ARRVAL(new_opts), 0, 0); ++ zend_update_property(this_ce, instance, ZEND_STRL("options"), &new_opts); + zval_ptr_dtor(&new_opts); + zval_ptr_dtor(&add_opts); + } + } + -void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC) ++void php_http_client_options_get_subr(zval *instance, char *key, size_t len, zval *return_value) + { - zend_class_entry *this_ce = Z_OBJCE_P(getThis()); - zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ zend_class_entry *this_ce = Z_OBJCE_P(instance); ++ zval *options, opts_tmp, *opts = zend_read_property(this_ce, instance, ZEND_STRL("options"), 0, &opts_tmp); + - if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) { - RETVAL_ZVAL(*options, 1, 0); ++ if ((Z_TYPE_P(opts) == IS_ARRAY) && (options = zend_symtable_str_find(Z_ARRVAL_P(opts), key, len))) { ++ RETVAL_ZVAL(options, 1, 0); + } + } + + static void queue_dtor(void *enqueued) + { + php_http_client_enqueue_t *e = enqueued; + + if (e->dtor) { + e->dtor(e); + } + } + -php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC) ++php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg) + { + php_http_client_t *free_h = NULL; + + if (!h) { + free_h = h = emalloc(sizeof(*h)); + } + memset(h, 0, sizeof(*h)); + + h->ops = ops; + if (rf) { + h->rf = rf; + } else if (ops->rsrc) { + h->rf = php_resource_factory_init(NULL, h->ops->rsrc, h, NULL); + } + zend_llist_init(&h->requests, sizeof(php_http_client_enqueue_t), queue_dtor, 0); + zend_llist_init(&h->responses, sizeof(void *), NULL, 0); - TSRMLS_SET_CTX(h->ts); + + if (h->ops->init) { + if (!(h = h->ops->init(h, init_arg))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize client"); - if (free_h) { - efree(free_h); - } ++ php_error_docref(NULL, E_WARNING, "Could not initialize client"); ++ PTR_FREE(free_h); + } + } + + return h; + } + + php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to) + { + if (from->ops->copy) { + return from->ops->copy(from, to); + } + + return NULL; + } + + void php_http_client_dtor(php_http_client_t *h) + { + php_http_client_reset(h); + + if (h->ops->dtor) { + h->ops->dtor(h); + } + + php_resource_factory_free(&h->rf); + } + + void php_http_client_free(php_http_client_t **h) { + if (*h) { + php_http_client_dtor(*h); + efree(*h); + *h = NULL; + } + } + + ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) + { - TSRMLS_FETCH_FROM_CTX(h->ts); - + if (h->ops->enqueue) { + if (php_http_client_enqueued(h, enqueue->request, NULL)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enqueue request; request already in queue"); ++ php_error_docref(NULL, E_WARNING, "Failed to enqueue request; request already in queue"); + return FAILURE; + } + return h->ops->enqueue(h, enqueue); + } + + return FAILURE; + } + + ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request) + { - TSRMLS_FETCH_FROM_CTX(h->ts); - + if (h->ops->dequeue) { + php_http_client_enqueue_t *enqueue = php_http_client_enqueued(h, request, NULL); + + if (!enqueue) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to dequeue request; request not in queue"); ++ php_error_docref(NULL, E_WARNING, "Failed to dequeue request; request not in queue"); + return FAILURE; + } + return h->ops->dequeue(h, enqueue); + } + return FAILURE; + } + + php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func) + { + zend_llist_element *el = NULL; + + if (compare_func) { + for (el = h->requests.head; el; el = el->next) { + if (compare_func((php_http_client_enqueue_t *) el->data, compare_arg)) { + break; + } + } + } else { + for (el = h->requests.head; el; el = el->next) { + if (((php_http_client_enqueue_t *) el->data)->request == compare_arg) { + break; + } + } + } + return el ? (php_http_client_enqueue_t *) el->data : NULL; + } + + ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout) + { + if (h->ops->wait) { + return h->ops->wait(h, custom_timeout); + } + + return FAILURE; + } + + int php_http_client_once(php_http_client_t *h) + { + if (h->ops->once) { + return h->ops->once(h); + } + + return FAILURE; + } + + ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h) + { + if (h->ops->exec) { + return h->ops->exec(h); + } + + return FAILURE; + } + + void php_http_client_reset(php_http_client_t *h) + { + if (h->ops->reset) { + h->ops->reset(h); + } + + zend_llist_clean(&h->requests); + zend_llist_clean(&h->responses); + } + + ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg) + { + if (h->ops->setopt) { + return h->ops->setopt(h, opt, arg); + } + + return FAILURE; + } + + ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr) + { + if (h->ops->getopt) { + return h->ops->getopt(h, opt, arg, res_ptr); + } + return FAILURE; + } + + zend_class_entry *php_http_client_class_entry; + static zend_object_handlers php_http_client_object_handlers; + -void php_http_client_object_free(void *object TSRMLS_DC) ++void php_http_client_object_free(zend_object *object) + { - php_http_client_object_t *o = (php_http_client_object_t *) object; ++ php_http_client_object_t *o = PHP_HTTP_OBJ(object, NULL); + + php_http_client_free(&o->client); + php_http_object_method_dtor(&o->notify); + php_http_object_method_free(&o->update); - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); ++ zend_object_std_dtor(object); + } + -zend_object_value php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client, php_http_client_object_t **ptr TSRMLS_DC) ++php_http_client_object_t *php_http_client_object_new_ex(zend_class_entry *ce, php_http_client_t *client) + { + php_http_client_object_t *o; + - o = ecalloc(1, sizeof(php_http_client_object_t)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + o->client = client; + - if (ptr) { - *ptr = o; - } ++ o->zo.handlers = &php_http_client_object_handlers; + - o->zv.handle = zend_objects_store_put(o, NULL, php_http_client_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_client_object_handlers; - - return o->zv; ++ return o; + } + -zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_client_object_new(zend_class_entry *ce) + { - return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_client_object_new_ex(ce, NULL)->zo; + } + -static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response TSRMLS_DC) ++static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response) + { - zval *new_hist, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0 TSRMLS_CC); - php_http_message_t *zipped = php_http_message_zip(response, request); - zend_object_value ov = php_http_message_object_new_ex(php_http_message_class_entry, zipped, NULL TSRMLS_CC); ++ zval new_hist, old_hist_tmp, *old_hist = zend_read_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), 0, &old_hist_tmp); ++ php_http_message_t *req_copy = php_http_message_copy(request, NULL); ++ php_http_message_t *res_copy = php_http_message_copy(response, NULL); ++ php_http_message_t *zipped = php_http_message_zip(res_copy, req_copy); ++ php_http_message_object_t *obj = php_http_message_object_new_ex(php_http_message_class_entry, zipped); + - MAKE_STD_ZVAL(new_hist); - ZVAL_OBJVAL(new_hist, ov, 0); ++ ZVAL_OBJ(&new_hist, &obj->zo); + + if (Z_TYPE_P(old_hist) == IS_OBJECT) { - php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC); ++ php_http_message_object_prepend(&new_hist, old_hist, 1); + } + - zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC); ++ zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), &new_hist); + zval_ptr_dtor(&new_hist); + } + + static ZEND_RESULT_CODE handle_response(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_message_t **response) + { + zend_bool dequeue = 0; + zval zclient; + php_http_message_t *msg; + php_http_client_progress_state_t *progress; - TSRMLS_FETCH_FROM_CTX(client->ts); + - INIT_PZVAL(&zclient); - ZVAL_OBJVAL(&zclient, ((php_http_client_object_t*) arg)->zv, 0); ++ ZVAL_OBJ(&zclient, &((php_http_client_object_t*) arg)->zo); + + if ((msg = *response)) { + php_http_message_object_t *msg_obj; - zval *info, *zresponse, *zrequest; ++ zval info, zresponse, zrequest, rec_hist_tmp; + HashTable *info_ht; + + /* ensure the message is of type response (could be uninitialized in case of early error, like DNS) */ + php_http_message_set_type(msg, PHP_HTTP_RESPONSE); + - if (z_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0 TSRMLS_CC))) { - handle_history(&zclient, e->request, *response TSRMLS_CC); ++ if (zend_is_true(zend_read_property(php_http_client_class_entry, &zclient, ZEND_STRL("recordHistory"), 0, &rec_hist_tmp))) { ++ handle_history(&zclient, e->request, *response); + } + + /* hard detach, redirects etc. are in the history */ + php_http_message_free(&msg->parent); + *response = NULL; + - MAKE_STD_ZVAL(zresponse); - ZVAL_OBJVAL(zresponse, php_http_message_object_new_ex(php_http_client_response_class_entry, msg, &msg_obj TSRMLS_CC), 0); - - MAKE_STD_ZVAL(zrequest); - ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1); ++ msg_obj = php_http_message_object_new_ex(php_http_client_response_class_entry, msg); ++ ZVAL_OBJ(&zresponse, &msg_obj->zo); ++ ZVAL_OBJECT(&zrequest, &((php_http_message_object_t *) e->opaque)->zo, 1); + - php_http_message_object_prepend(zresponse, zrequest, 1 TSRMLS_CC); ++ php_http_message_object_prepend(&zresponse, &zrequest, 1); + - MAKE_STD_ZVAL(info); - object_init(info); - info_ht = HASH_OF(info); ++ object_init(&info); ++ info_ht = HASH_OF(&info); + php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, e->request, &info_ht); - zend_update_property(php_http_client_response_class_entry, zresponse, ZEND_STRL("transferInfo"), info TSRMLS_CC); ++ zend_update_property(php_http_client_response_class_entry, &zresponse, ZEND_STRL("transferInfo"), &info); + zval_ptr_dtor(&info); + - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); ++ Z_ADDREF(zresponse); + zend_llist_add_element(&client->responses, &msg_obj); + + if (e->closure.fci.size) { - zval *retval = NULL; ++ zval retval; + zend_error_handling zeh; + - zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 1, &zresponse); - zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC); - zend_fcall_info_call(&e->closure.fci, &e->closure.fcc, &retval, NULL TSRMLS_CC); - zend_restore_error_handling(&zeh TSRMLS_CC); - zend_fcall_info_argn(&e->closure.fci TSRMLS_CC, 0); ++ ZVAL_UNDEF(&retval); ++ zend_fcall_info_argn(&e->closure.fci, 1, &zresponse); ++ zend_replace_error_handling(EH_NORMAL, NULL, &zeh); ++ zend_fcall_info_call(&e->closure.fci, &e->closure.fcc, &retval, NULL); ++ zend_restore_error_handling(&zeh); ++ zend_fcall_info_argn(&e->closure.fci, 0); + - if (retval) { - if (Z_TYPE_P(retval) == IS_BOOL) { - dequeue = Z_BVAL_P(retval); - } - zval_ptr_dtor(&retval); ++ if (Z_TYPE(retval) == IS_TRUE) { ++ dequeue = 1; + } ++ zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zresponse); + zval_ptr_dtor(&zrequest); + } + + if (SUCCESS == php_http_client_getopt(client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, e->request, &progress)) { + progress->info = "finished"; + progress->finished = 1; + client->callback.progress.func(client->callback.progress.arg, client, e, progress); + } + + if (dequeue) { + php_http_client_dequeue(client, e->request); + } + + return SUCCESS; + } + + static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress) + { - zval *zrequest, *zprogress, *zclient, **args[2]; ++ zval zclient, args[2]; + php_http_client_object_t *client_obj = arg; + zend_error_handling zeh; - TSRMLS_FETCH_FROM_CTX(client->ts); - - MAKE_STD_ZVAL(zclient); - ZVAL_OBJVAL(zclient, client_obj->zv, 1); - - MAKE_STD_ZVAL(zrequest); - ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1); - args[0] = &zrequest; - - MAKE_STD_ZVAL(zprogress); - object_init(zprogress); - add_property_bool(zprogress, "started", progress->started); - add_property_bool(zprogress, "finished", progress->finished); - add_property_string(zprogress, "info", STR_PTR(progress->info), 1); - add_property_double(zprogress, "dltotal", progress->dl.total); - add_property_double(zprogress, "dlnow", progress->dl.now); - add_property_double(zprogress, "ultotal", progress->ul.total); - add_property_double(zprogress, "ulnow", progress->ul.now); - args[1] = &zprogress; - - zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC); - php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ ++ ZVAL_OBJECT(&zclient, &client_obj->zo, 1); ++ ZVAL_OBJECT(&args[0], &((php_http_message_object_t *) e->opaque)->zo, 1); ++ object_init(&args[1]); ++ add_property_bool(&args[1], "started", progress->started); ++ add_property_bool(&args[1], "finished", progress->finished); ++ add_property_string(&args[1], "info", STR_PTR(progress->info)); ++ add_property_double(&args[1], "dltotal", progress->dl.total); ++ add_property_double(&args[1], "dlnow", progress->dl.now); ++ add_property_double(&args[1], "ultotal", progress->ul.total); ++ add_property_double(&args[1], "ulnow", progress->ul.now); ++ ++ zend_replace_error_handling(EH_NORMAL, NULL, &zeh); ++ php_http_object_method_call(&client_obj->notify, &zclient, NULL, 2, args); ++ zend_restore_error_handling(&zeh); + + zval_ptr_dtor(&zclient); - zval_ptr_dtor(&zrequest); - zval_ptr_dtor(&zprogress); ++ zval_ptr_dtor(&args[0]); ++ zval_ptr_dtor(&args[1]); + } + + static void response_dtor(void *data) + { + php_http_message_object_t *msg_obj = *(php_http_message_object_t **) data; - TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts); + - zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC); ++ zend_objects_store_del(&msg_obj->zo); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_construct, 0, 0, 0) + ZEND_ARG_INFO(0, driver) + ZEND_ARG_INFO(0, persistent_handle_id) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, __construct) + { - char *driver_str = NULL, *persistent_handle_str = NULL; - int driver_len = 0, persistent_handle_len = 0; - php_http_client_driver_t driver; ++ zend_string *driver_name = NULL, *persistent_handle_name = NULL; ++ php_http_client_driver_t *driver; + php_resource_factory_t *rf = NULL; + php_http_client_object_t *obj; - zval *os; ++ zval os; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &driver_str, &driver_len, &persistent_handle_str, &persistent_handle_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|S!S!", &driver_name, &persistent_handle_name), invalid_arg, return); + - if (SUCCESS != php_http_client_driver_get(driver_str, driver_len, &driver)) { - php_http_throw(unexpected_val, "Failed to locate \"%s\" client request handler", driver_str); ++ if (!zend_hash_num_elements(&php_http_client_drivers)) { ++ php_http_throw(unexpected_val, "No http\\Client drivers available", NULL); ++ return; ++ } ++ if (!(driver = php_http_client_driver_get(driver_name))) { ++ php_http_throw(unexpected_val, "Failed to locate \"%s\" client request handler", driver_name ? driver_name->val : "default"); + return; + } + - MAKE_STD_ZVAL(os); - object_init_ex(os, spl_ce_SplObjectStorage); - zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), os TSRMLS_CC); ++ object_init_ex(&os, spl_ce_SplObjectStorage); ++ zend_update_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), &os); + zval_ptr_dtor(&os); + - if (persistent_handle_len) { - char *name_str; - size_t name_len; ++ if (persistent_handle_name) { + php_persistent_handle_factory_t *pf; + - name_len = spprintf(&name_str, 0, "http\\Client\\%s", driver.name_str); - php_http_pretty_key(name_str + sizeof("http\\Client"), driver.name_len, 1, 1); - - if ((pf = php_persistent_handle_concede(NULL , name_str, name_len, persistent_handle_str, persistent_handle_len, NULL, NULL TSRMLS_CC))) { ++ if ((pf = php_persistent_handle_concede(NULL, driver->client_name, persistent_handle_name, NULL, NULL))) { + rf = php_persistent_handle_resource_factory_init(NULL, pf); + } - - efree(name_str); + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + - php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return); ++ php_http_expect(obj->client = php_http_client_init(NULL, driver->client_ops, rf, NULL), runtime, return); + - php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC); ++ php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify")); + + obj->client->callback.response.func = handle_response; + obj->client->callback.response.arg = obj; + obj->client->callback.progress.func = handle_progress; + obj->client->callback.progress.arg = obj; + + obj->client->responses.dtor = response_dtor; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_reset, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, reset) + { + php_http_client_object_t *obj; + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + obj->iterator = 0; + php_http_client_reset(obj->client); + + RETVAL_ZVAL(getThis(), 1, 0); + } + -static HashTable *combined_options(zval *client, zval *request TSRMLS_DC) ++static HashTable *combined_options(zval *client, zval *request) + { + HashTable *options; - int num_options = 0; - zval *z_roptions = NULL, *z_coptions = zend_read_property(php_http_client_class_entry, client, ZEND_STRL("options"), 0 TSRMLS_CC); ++ unsigned num_options = 0; ++ zval z_roptions, z_options_tmp, *z_coptions = zend_read_property(php_http_client_class_entry, client, ZEND_STRL("options"), 0, &z_options_tmp); + + if (Z_TYPE_P(z_coptions) == IS_ARRAY) { + num_options = zend_hash_num_elements(Z_ARRVAL_P(z_coptions)); + } - zend_call_method_with_0_params(&request, NULL, NULL, "getOptions", &z_roptions); - if (z_roptions && Z_TYPE_P(z_roptions) == IS_ARRAY) { - int num = zend_hash_num_elements(Z_ARRVAL_P(z_roptions)); ++ ZVAL_UNDEF(&z_roptions); ++ zend_call_method_with_0_params(request, NULL, NULL, "getOptions", &z_roptions); ++ if (Z_TYPE(z_roptions) == IS_ARRAY) { ++ unsigned num = zend_hash_num_elements(Z_ARRVAL(z_roptions)); + if (num > num_options) { + num_options = num; + } + } + ALLOC_HASHTABLE(options); + ZEND_INIT_SYMTABLE_EX(options, num_options, 0); + if (Z_TYPE_P(z_coptions) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(z_coptions), options); + } - if (z_roptions) { - if (Z_TYPE_P(z_roptions) == IS_ARRAY) { - array_join(Z_ARRVAL_P(z_roptions), options, 0, 0); - } - zval_ptr_dtor(&z_roptions); ++ if (Z_TYPE(z_roptions) == IS_ARRAY) { ++ array_join(Z_ARRVAL(z_roptions), options, 0, 0); + } ++ zval_ptr_dtor(&z_roptions); ++ + return options; + } + + static void msg_queue_dtor(php_http_client_enqueue_t *e) + { + php_http_message_object_t *msg_obj = e->opaque; - TSRMLS_FETCH_FROM_CTX(msg_obj->message->ts); + - zend_objects_store_del_ref_by_handle_ex(msg_obj->zv.handle, msg_obj->zv.handlers TSRMLS_CC); ++ zend_objects_store_del(&msg_obj->zo); + zend_hash_destroy(e->options); + FREE_HASHTABLE(e->options); + + if (e->closure.fci.size) { + zval_ptr_dtor(&e->closure.fci.function_name); - if (e->closure.fci.object_ptr) { - zval_ptr_dtor(&e->closure.fci.object_ptr); ++ if (e->closure.fci.object) { ++ zend_objects_store_del(e->closure.fci.object); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enqueue, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0) + ZEND_ARG_INFO(0, callable) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, enqueue) + { + zval *request; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + php_http_client_object_t *obj; + php_http_message_object_t *msg_obj; + php_http_client_enqueue_t q; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ msg_obj = PHP_HTTP_OBJ(NULL, request); + + if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) { + php_http_throw(bad_method_call, "Failed to enqueue request; request already in queue", NULL); + return; + } + + q.request = msg_obj->message; - q.options = combined_options(getThis(), request TSRMLS_CC); ++ q.options = combined_options(getThis(), request); + q.dtor = msg_queue_dtor; + q.opaque = msg_obj; + q.closure.fci = fci; + q.closure.fcc = fcc; + + if (fci.size) { - Z_ADDREF_P(fci.function_name); - if (fci.object_ptr) { - Z_ADDREF_P(fci.object_ptr); ++ Z_TRY_ADDREF(fci.function_name); ++ if (fci.object) { ++ ++GC_REFCOUNT(fci.object); + } + } + - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); ++ Z_ADDREF_P(request); + + php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime, + msg_queue_dtor(&q); + return; + ); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_dequeue, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, dequeue) + { + zval *request; + php_http_client_object_t *obj; + php_http_message_object_t *msg_obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request, php_http_client_request_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ msg_obj = PHP_HTTP_OBJ(NULL, request); + + if (!php_http_client_enqueued(obj->client, msg_obj->message, NULL)) { + php_http_throw(bad_method_call, "Failed to dequeue request; request not in queue", NULL); + return; + } + + php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_requeue, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0) + ZEND_ARG_INFO(0, callable) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, requeue) + { + zval *request; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + php_http_client_object_t *obj; + php_http_message_object_t *msg_obj; + php_http_client_enqueue_t q; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|f", &request, php_http_client_request_class_entry, &fci, &fcc), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ msg_obj = PHP_HTTP_OBJ(NULL, request); + + if (php_http_client_enqueued(obj->client, msg_obj->message, NULL)) { + php_http_expect(SUCCESS == php_http_client_dequeue(obj->client, msg_obj->message), runtime, return); + } + + q.request = msg_obj->message; - q.options = combined_options(getThis(), request TSRMLS_CC); ++ q.options = combined_options(getThis(), request); + q.dtor = msg_queue_dtor; + q.opaque = msg_obj; + q.closure.fci = fci; + q.closure.fcc = fcc; + + if (fci.size) { - Z_ADDREF_P(fci.function_name); - if (fci.object_ptr) { - Z_ADDREF_P(fci.object_ptr); ++ Z_TRY_ADDREF(fci.function_name); ++ if (fci.object) { ++ ++GC_REFCOUNT(fci.object); + } + } + - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); ++ Z_ADDREF_P(request); + + php_http_expect(SUCCESS == php_http_client_enqueue(obj->client, &q), runtime, + msg_queue_dtor(&q); + return; + ); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_count, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, count) + { - long count_mode = -1; ++ zend_long count_mode = -1; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) { - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &count_mode)) { ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + RETVAL_LONG(zend_llist_count(&obj->client->requests)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getResponse, 0, 0, 0) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getResponse) + { + zval *zrequest = NULL; + php_http_client_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &zrequest, php_http_client_request_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|O", &zrequest, php_http_client_request_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (zrequest) { + /* lookup the response with the request */ + zend_llist_element *el = NULL; - php_http_message_object_t *req_obj = zend_object_store_get_object(zrequest TSRMLS_CC); ++ php_http_message_object_t *req_obj = PHP_HTTP_OBJ(NULL, zrequest); + + for (el = obj->client->responses.head; el; el = el->next) { + php_http_message_object_t *response_obj = *(php_http_message_object_t **) el->data; + + if (response_obj->message->parent == req_obj->message) { - RETURN_OBJVAL(response_obj->zv, 1); ++ RETURN_OBJECT(&response_obj->zo, 1); + } + } + + /* not found for the request! */ + php_http_throw(unexpected_val, "Could not find response for the request", NULL); + return; + } + + /* pop off the last response */ + if (obj->client->responses.tail) { + php_http_message_object_t *response_obj = *(php_http_message_object_t **) obj->client->responses.tail->data; + + /* pop off and go */ + if (response_obj) { - RETVAL_OBJVAL(response_obj->zv, 1); ++ RETVAL_OBJECT(&response_obj->zo, 1); + zend_llist_remove_tail(&obj->client->responses); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getHistory, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getHistory) + { - zval *zhistory; ++ zval zhistory_tmp, *zhistory; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - zhistory = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0 TSRMLS_CC); ++ zhistory = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("history"), 0, &zhistory_tmp); + RETVAL_ZVAL(zhistory, 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_send, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, send) + { + php_http_client_object_t *obj; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + php_http_expect(SUCCESS == php_http_client_exec(obj->client), runtime, return); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_once, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, once) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + RETURN_BOOL(0 < php_http_client_once(obj->client)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_wait, 0, 0, 0) + ZEND_ARG_INFO(0, timeout) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, wait) + { + double timeout = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|d", &timeout)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|d", &timeout)) { + struct timeval timeout_val; - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + timeout_val.tv_sec = (time_t) timeout; + timeout_val.tv_usec = PHP_HTTP_USEC(timeout) % PHP_HTTP_MCROSEC; + + RETURN_BOOL(SUCCESS == php_http_client_wait(obj->client, timeout > 0 ? &timeout_val : NULL)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_configure, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, settings, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, configure) + { + HashTable *settings = NULL; + php_http_client_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H!", &settings), invalid_arg, return); - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|H!", &settings), invalid_arg, return); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_CONFIGURATION, settings), unexpected_val, return); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enablePipelining, 0, 0, 0) + ZEND_ARG_INFO(0, enable) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, enablePipelining) + { + zend_bool enable = 1; + php_http_client_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING, &enable), unexpected_val, return); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_enableEvents, 0, 0, 0) + ZEND_ARG_INFO(0, enable) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, enableEvents) + { + zend_bool enable = 1; + php_http_client_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &enable), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &enable), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + php_http_expect(SUCCESS == php_http_client_setopt(obj->client, PHP_HTTP_CLIENT_OPT_USE_EVENTS, &enable), unexpected_val, return); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + struct notify_arg { + php_http_object_method_t *cb; - zval **args[3]; ++ zval args[3]; + int argc; + }; + -static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC) ++static int notify(zend_object_iterator *iter, void *puser) + { - zval **observer = NULL; ++ zval *observer; + struct notify_arg *arg = puser; + - iter->funcs->get_current_data(iter, &observer TSRMLS_CC); - if (observer) { - return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC); ++ if ((observer = iter->funcs->get_current_data(iter))) { ++ if (SUCCESS == php_http_object_method_call(arg->cb, observer, NULL, arg->argc, arg->args)) { ++ return ZEND_HASH_APPLY_KEEP; ++ } + } - return FAILURE; ++ return ZEND_HASH_APPLY_STOP; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify, 0, 0, 0) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, notify) + { - zval *request = NULL, *zprogress = NULL, *observers; ++ zval *request = NULL, *zprogress = NULL, observers_tmp, *observers; + php_http_client_object_t *client_obj; + struct notify_arg arg = {NULL}; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return); + - client_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); ++ client_obj = PHP_HTTP_OBJ(NULL, getThis()); ++ observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp); + + if (Z_TYPE_P(observers) != IS_OBJECT) { + php_http_throw(unexpected_val, "Observer storage is corrupted", NULL); + return; + } + + if (client_obj->update) { + arg.cb = client_obj->update; - - Z_ADDREF_P(getThis()); - arg.args[0] = &getThis(); ++ ZVAL_COPY(&arg.args[0], getThis()); + arg.argc = 1; + + if (request) { - Z_ADDREF_P(request); - arg.args[1] = &request; ++ ZVAL_COPY(&arg.args[1], request); + arg.argc += 1; + } - + if (zprogress) { - Z_ADDREF_P(zprogress); - arg.args[2] = &zprogress; ++ ZVAL_COPY(&arg.args[2], zprogress); + arg.argc += 1; + } + - spl_iterator_apply(observers, notify, &arg TSRMLS_CC); ++ spl_iterator_apply(observers, notify, &arg); + - zval_ptr_dtor(&getThis()); ++ zval_ptr_dtor(getThis()); + if (request) { - zval_ptr_dtor(&request); ++ zval_ptr_dtor(request); + } + if (zprogress) { - zval_ptr_dtor(&zprogress); ++ zval_ptr_dtor(zprogress); + } + } + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_attach, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, attach) + { - zval *observers, *observer, *retval = NULL; ++ zval observers_tmp, *observers, *observer, retval; + php_http_client_object_t *client_obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer, spl_ce_SplObserver), invalid_arg, return); + - client_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); ++ client_obj = PHP_HTTP_OBJ(NULL, getThis()); ++ observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp); + + if (Z_TYPE_P(observers) != IS_OBJECT) { + php_http_throw(unexpected_val, "Observer storage is corrupted", NULL); + return; + } + + if (!client_obj->update) { - client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC); ++ client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update")); + } + - zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer); - if (retval) { - zval_ptr_dtor(&retval); - } ++ ZVAL_UNDEF(&retval); ++ zend_call_method_with_1_params(observers, NULL, NULL, "attach", &retval, observer); ++ zval_ptr_dtor(&retval); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_detach, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, observer, SplObserver, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, detach) + { - zval *observers, *observer, *retval = NULL; ++ zval observers_tmp, *observers, *observer, retval; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &observer, spl_ce_SplObserver), invalid_arg, return); + - observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); ++ observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp); + + if (Z_TYPE_P(observers) != IS_OBJECT) { + php_http_throw(unexpected_val, "Observer storage is corrupted", NULL); + return; + } + - zend_call_method_with_1_params(&observers, NULL, NULL, "detach", &retval, observer); - if (retval) { - zval_ptr_dtor(&retval); - } ++ ZVAL_UNDEF(&retval); ++ zend_call_method_with_1_params(observers, NULL, NULL, "detach", &retval, observer); ++ zval_ptr_dtor(&retval); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getObservers, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getObservers) + { - zval *observers; ++ zval observers_tmp, *observers; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); ++ observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0, &observers_tmp); + + if (Z_TYPE_P(observers) != IS_OBJECT) { + php_http_throw(unexpected_val, "Observer storage is corrupted", NULL); + return; + } + + RETVAL_ZVAL(observers, 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getProgressInfo, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getProgressInfo) + { + zval *request; + php_http_client_object_t *obj; + php_http_message_object_t *req_obj; + php_http_client_progress_state_t *progress; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request, php_http_client_request_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - req_obj = zend_object_store_get_object(request TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ req_obj = PHP_HTTP_OBJ(NULL, request); + + php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, req_obj->message, &progress), unexpected_val, return); + + object_init(return_value); + add_property_bool(return_value, "started", progress->started); + add_property_bool(return_value, "finished", progress->finished); - add_property_string(return_value, "info", STR_PTR(progress->info), 1); ++ add_property_string(return_value, "info", STR_PTR(progress->info)); + add_property_double(return_value, "dltotal", progress->dl.total); + add_property_double(return_value, "dlnow", progress->dl.now); + add_property_double(return_value, "ultotal", progress->ul.total); + add_property_double(return_value, "ulnow", progress->ul.now); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getTransferInfo, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, request, http\\Client\\Request, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getTransferInfo) + { + zval *request; + HashTable *info; + php_http_client_object_t *obj; + php_http_message_object_t *req_obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &request, php_http_client_request_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &request, php_http_client_request_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - req_obj = zend_object_store_get_object(request TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ req_obj = PHP_HTTP_OBJ(NULL, request); + + object_init(return_value); + info = HASH_OF(return_value); + php_http_expect(SUCCESS == php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, req_obj->message, &info), unexpected_val, return); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, options, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, setOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set(getThis(), opts TSRMLS_CC); ++ php_http_client_options_set(getThis(), opts); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getOptions, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getOptions) + { + if (SUCCESS == zend_parse_parameters_none()) { - zval *options = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ zval options_tmp, *options = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("options"), 0, &options_tmp); + RETVAL_ZVAL(options, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setSslOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, ssl_option, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, setSslOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts, 1); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addSslOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, ssl_options, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, addSslOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts, 0); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getSslOptions, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getSslOptions) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC); ++ php_http_client_options_get_subr(getThis(), ZEND_STRL("ssl"), return_value); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_setCookies, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, cookies, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, setCookies) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("cookies"), opts, 1); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_addCookies, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, cookies, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, addCookies) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("cookies"), opts, 0); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getCookies, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getCookies) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_options_get_subr(getThis(), ZEND_STRS("cookies"), return_value TSRMLS_CC); ++ php_http_client_options_get_subr(getThis(), ZEND_STRL("cookies"), return_value); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers, 0, 0, 0) + ZEND_END_ARG_INFO(); -static PHP_METHOD(HttpClient, getAvailableDrivers) { ++static PHP_METHOD(HttpClient, getAvailableDrivers) ++{ + if (SUCCESS == zend_parse_parameters_none()) { + array_init(return_value); - php_http_client_driver_list(Z_ARRVAL_P(return_value) TSRMLS_CC); ++ php_http_client_driver_list(Z_ARRVAL_P(return_value)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableOptions, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getAvailableOptions) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + array_init(return_value); + php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, NULL, &Z_ARRVAL_P(return_value)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableConfiguration, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClient, getAvailableConfiguration) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_client_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + array_init(return_value); + php_http_client_getopt(obj->client, PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION, NULL, &Z_ARRVAL_P(return_value)); + } + } + + static zend_function_entry php_http_client_methods[] = { + PHP_ME(HttpClient, __construct, ai_HttpClient_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpClient, reset, ai_HttpClient_reset, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, enqueue, ai_HttpClient_enqueue, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, dequeue, ai_HttpClient_dequeue, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, requeue, ai_HttpClient_requeue, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, count, ai_HttpClient_count, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, send, ai_HttpClient_send, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, once, ai_HttpClient_once, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, wait, ai_HttpClient_wait, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getResponse, ai_HttpClient_getResponse, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getHistory, ai_HttpClient_getHistory, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, configure, ai_HttpClient_configure, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, enablePipelining, ai_HttpClient_enablePipelining, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + PHP_ME(HttpClient, enableEvents, ai_HttpClient_enableEvents, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + PHP_ME(HttpClient, notify, ai_HttpClient_notify, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, attach, ai_HttpClient_attach, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, detach, ai_HttpClient_detach, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getObservers, ai_HttpClient_getObservers, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getProgressInfo, ai_HttpClient_getProgressInfo, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getTransferInfo, ai_HttpClient_getTransferInfo, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, setOptions, ai_HttpClient_setOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getOptions, ai_HttpClient_getOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, setSslOptions, ai_HttpClient_setSslOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, addSslOptions, ai_HttpClient_addSslOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getSslOptions, ai_HttpClient_getSslOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, setCookies, ai_HttpClient_setCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, addCookies, ai_HttpClient_addCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getCookies, ai_HttpClient_getCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getAvailableDrivers, ai_HttpClient_getAvailableDrivers, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpClient, getAvailableOptions, ai_HttpClient_getAvailableOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClient, getAvailableConfiguration, ai_HttpClient_getAvailableConfiguration, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + PHP_MINIT_FUNCTION(http_client) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Client", php_http_client_methods); - php_http_client_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); ++ php_http_client_class_entry = zend_register_internal_class_ex(&ce, NULL); + php_http_client_class_entry->create_object = php_http_client_object_new; - zend_class_implements(php_http_client_class_entry TSRMLS_CC, 2, spl_ce_SplSubject, spl_ce_Countable); ++ zend_class_implements(php_http_client_class_entry, 2, spl_ce_SplSubject, spl_ce_Countable); + memcpy(&php_http_client_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ++ php_http_client_object_handlers.offset = XtOffsetOf(php_http_client_object_t, zo); ++ php_http_client_object_handlers.free_obj = php_http_client_object_free; + php_http_client_object_handlers.clone_obj = NULL; - zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE TSRMLS_CC); - zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ++ zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("observers"), ZEND_ACC_PRIVATE); ++ zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_client_class_entry, ZEND_STRL("history"), ZEND_ACC_PROTECTED); ++ zend_declare_property_bool(php_http_client_class_entry, ZEND_STRL("recordHistory"), 0, ZEND_ACC_PUBLIC); + - zend_hash_init(&php_http_client_drivers, 2, NULL, NULL, 1); ++ zend_hash_init(&php_http_client_drivers, 2, NULL, php_http_client_driver_hash_dtor, 1); + + return SUCCESS; + } + + PHP_MSHUTDOWN_FUNCTION(http_client) + { + zend_hash_destroy(&php_http_client_drivers); + return SUCCESS; + } + + /* + * 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 --cc src/php_http_client.h index 0000000,f4a5b59..792581a mode 000000,100644..100644 --- a/src/php_http_client.h +++ b/src/php_http_client.h @@@ -1,0 -1,161 +1,157 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_CLIENT_H + #define PHP_HTTP_CLIENT_H + + typedef enum php_http_client_setopt_opt { + PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING, + PHP_HTTP_CLIENT_OPT_USE_EVENTS, + PHP_HTTP_CLIENT_OPT_CONFIGURATION, + } php_http_client_setopt_opt_t; + + typedef enum php_http_client_getopt_opt { + PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, /* php_http_client_enqueue_t*, php_http_client_progress_state_t** */ + PHP_HTTP_CLIENT_OPT_TRANSFER_INFO, /* php_http_client_enqueue_t*, HashTable* */ + PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS, /* NULL, HashTable* */ + PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION,/* NULL, HashTable */ + } php_http_client_getopt_opt_t; + + typedef struct php_http_client_enqueue { + php_http_message_t *request; /* unique */ + HashTable *options; + void (*dtor)(struct php_http_client_enqueue *); + void *opaque; + struct { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + } closure; + } php_http_client_enqueue_t; + + typedef struct php_http_client *(*php_http_client_init_func_t)(struct php_http_client *p, void *init_arg); + typedef struct php_http_client *(*php_http_client_copy_func_t)(struct php_http_client *from, struct php_http_client *to); + typedef void (*php_http_client_dtor_func_t)(struct php_http_client *p); + typedef void (*php_http_client_reset_func_t)(struct php_http_client *p); + typedef ZEND_RESULT_CODE (*php_http_client_exec_func_t)(struct php_http_client *p); + typedef int (*php_http_client_once_func_t)(struct php_http_client *p); + typedef ZEND_RESULT_CODE (*php_http_client_wait_func_t)(struct php_http_client *p, struct timeval *custom_timeout); + typedef ZEND_RESULT_CODE (*php_http_client_enqueue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue); + typedef ZEND_RESULT_CODE (*php_http_client_dequeue_func_t)(struct php_http_client *p, php_http_client_enqueue_t *enqueue); + typedef ZEND_RESULT_CODE (*php_http_client_setopt_func_t)(struct php_http_client *p, php_http_client_setopt_opt_t opt, void *arg); + typedef ZEND_RESULT_CODE (*php_http_client_getopt_func_t)(struct php_http_client *h, php_http_client_getopt_opt_t opt, void *arg, void **res); + + typedef struct php_http_client_ops { + php_resource_factory_ops_t *rsrc; + php_http_client_init_func_t init; + php_http_client_copy_func_t copy; + php_http_client_dtor_func_t dtor; + php_http_client_reset_func_t reset; + php_http_client_exec_func_t exec; + php_http_client_wait_func_t wait; + php_http_client_once_func_t once; + php_http_client_enqueue_func_t enqueue; + php_http_client_dequeue_func_t dequeue; + php_http_client_setopt_func_t setopt; + php_http_client_getopt_func_t getopt; + } php_http_client_ops_t; + + typedef struct php_http_client_driver { - const char *name_str; - size_t name_len; ++ zend_string *driver_name; ++ zend_string *client_name; ++ zend_string *request_name; + php_http_client_ops_t *client_ops; + } php_http_client_driver_t; + + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_add(php_http_client_driver_t *driver); -PHP_HTTP_API ZEND_RESULT_CODE php_http_client_driver_get(const char *name_str, size_t name_len, php_http_client_driver_t *driver); ++PHP_HTTP_API php_http_client_driver_t *php_http_client_driver_get(zend_string *name); + + typedef struct php_http_client_progress_state { + struct { + double now; + double total; + } ul; + struct { + double now; + double total; + } dl; + const char *info; + unsigned started:1; + unsigned finished:1; + } php_http_client_progress_state_t; + + typedef ZEND_RESULT_CODE (*php_http_client_response_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_message_t **response); + typedef void (*php_http_client_progress_callback_t)(void *arg, struct php_http_client *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *state); + + typedef struct php_http_client { + void *ctx; + php_resource_factory_t *rf; + php_http_client_ops_t *ops; + + struct { + struct { + php_http_client_response_callback_t func; + void *arg; + } response; + struct { + php_http_client_progress_callback_t func; + void *arg; + } progress; + } callback; + + zend_llist requests; + zend_llist responses; - -#ifdef ZTS - void ***ts; -#endif + } php_http_client_t; + + PHP_HTTP_API zend_class_entry *php_http_client_class_entry; + + typedef struct php_http_client_object { - zend_object zo; - zend_object_value zv; + php_http_client_t *client; - long iterator; + php_http_object_method_t *update; + php_http_object_method_t notify; ++ long iterator; ++ zend_object zo; + } php_http_client_object_t; + -PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC); ++PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg); + PHP_HTTP_API php_http_client_t *php_http_client_copy(php_http_client_t *from, php_http_client_t *to); + PHP_HTTP_API void php_http_client_dtor(php_http_client_t *h); + PHP_HTTP_API void php_http_client_free(php_http_client_t **h); + + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue); + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_dequeue(php_http_client_t *h, php_http_message_t *request); + + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_wait(php_http_client_t *h, struct timeval *custom_timeout); + PHP_HTTP_API int php_http_client_once(php_http_client_t *h); + + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_exec(php_http_client_t *h); + PHP_HTTP_API void php_http_client_reset(php_http_client_t *h); + + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg); + PHP_HTTP_API ZEND_RESULT_CODE php_http_client_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void *res_ptr); + + typedef int (*php_http_client_enqueue_cmp_func_t)(php_http_client_enqueue_t *cmp, void *arg); + /* compare with request message pointer if compare_func is NULL */ + PHP_HTTP_API php_http_client_enqueue_t *php_http_client_enqueued(php_http_client_t *h, void *compare_arg, php_http_client_enqueue_cmp_func_t compare_func); + + PHP_MINIT_FUNCTION(http_client); + PHP_MSHUTDOWN_FUNCTION(http_client); + + #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 --cc src/php_http_client_curl.c index 0000000,fed92fc..85f12b7 mode 000000,100644..100644 --- a/src/php_http_client_curl.c +++ b/src/php_http_client_curl.c @@@ -1,0 -1,2599 +1,2628 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + #include "php_http_client.h" + + #if PHP_HTTP_HAVE_CURL + + #if PHP_HTTP_HAVE_EVENT + # if !PHP_HTTP_HAVE_EVENT2 && /* just be really sure */ !(LIBEVENT_VERSION_NUMBER >= 0x02000000) + # include + # define event_base_new event_init + # define event_assign(e, b, s, a, cb, d) do {\ + event_set(e, s, a, cb, d); \ + event_base_set(b, e); \ + } while(0) + # else + # if PHP_HTTP_HAVE_EVENT2 + # include + # include + # else + # error "libevent presence is unknown" + # endif + # endif + # ifndef DBG_EVENTS + # define DBG_EVENTS 0 + # endif + #endif + + #ifdef PHP_HTTP_HAVE_OPENSSL + # include + #endif + #ifdef PHP_HTTP_HAVE_GNUTLS + # include + #endif + + typedef struct php_http_client_curl { + CURLM *handle; + + int unfinished; /* int because of curl_multi_perform() */ + + #if PHP_HTTP_HAVE_EVENT + struct event_base *evbase; + struct event *timeout; + unsigned useevents:1; + #endif + } php_http_client_curl_t; + + typedef struct php_http_client_curl_handler { + CURL *handle; + php_resource_factory_t *rf; + php_http_client_t *client; + php_http_client_progress_state_t progress; + php_http_client_enqueue_t queue; + + struct { + php_http_buffer_t headers; + php_http_message_body_t *body; + } response; + + struct { + HashTable cache; + + struct curl_slist *proxyheaders; + struct curl_slist *headers; + struct curl_slist *resolve; + php_http_buffer_t cookies; + php_http_buffer_t ranges; + - long redirects; - unsigned range_request:1; - unsigned encode_cookies:1; - + struct { + uint count; + double delay; + } retry; + ++ long redirects; ++ unsigned range_request:1; ++ unsigned encode_cookies:1; ++ + } options; + + } php_http_client_curl_handler_t; + + typedef struct php_http_curle_storage { + char *url; + char *cookiestore; + CURLcode errorcode; + char errorbuffer[0x100]; + } php_http_curle_storage_t; + + static inline php_http_curle_storage_t *php_http_curle_get_storage(CURL *ch) { + php_http_curle_storage_t *st = NULL; + + curl_easy_getinfo(ch, CURLINFO_PRIVATE, &st); + + if (!st) { + st = pecalloc(1, sizeof(*st), 1); + curl_easy_setopt(ch, CURLOPT_PRIVATE, st); + curl_easy_setopt(ch, CURLOPT_ERRORBUFFER, st->errorbuffer); + } + + return st; + } + -static void *php_http_curle_ctor(void *opaque, void *init_arg TSRMLS_DC) ++static void *php_http_curle_ctor(void *opaque, void *init_arg) + { + void *ch; + + if ((ch = curl_easy_init())) { + php_http_curle_get_storage(ch); + return ch; + } + return NULL; + } + -static void *php_http_curle_copy(void *opaque, void *handle TSRMLS_DC) ++static void *php_http_curle_copy(void *opaque, void *handle) + { + void *ch; + + if ((ch = curl_easy_duphandle(handle))) { + curl_easy_reset(ch); + php_http_curle_get_storage(ch); + return ch; + } + return NULL; + } + -static void php_http_curle_dtor(void *opaque, void *handle TSRMLS_DC) ++static void php_http_curle_dtor(void *opaque, void *handle) + { + php_http_curle_storage_t *st = php_http_curle_get_storage(handle); + + curl_easy_cleanup(handle); + + if (st) { + if (st->url) { + pefree(st->url, 1); + } + if (st->cookiestore) { + pefree(st->cookiestore, 1); + } + pefree(st, 1); + } + } + + static php_resource_factory_ops_t php_http_curle_resource_factory_ops = { + php_http_curle_ctor, + php_http_curle_copy, + php_http_curle_dtor + }; + -static void *php_http_curlm_ctor(void *opaque, void *init_arg TSRMLS_DC) ++static void *php_http_curlm_ctor(void *opaque, void *init_arg) + { + return curl_multi_init(); + } + -static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC) ++static void php_http_curlm_dtor(void *opaque, void *handle) + { + curl_multi_cleanup(handle); + } + + static php_resource_factory_ops_t php_http_curlm_resource_factory_ops = { + php_http_curlm_ctor, + NULL, + php_http_curlm_dtor + }; + + /* curl callbacks */ + + static size_t php_http_curle_read_callback(void *data, size_t len, size_t n, void *ctx) + { + php_http_message_body_t *body = ctx; + - if (body && body->stream_id) { ++ if (body && body->res) { + php_stream *s = php_http_message_body_stream(body); + + if (s) { - TSRMLS_FETCH_FROM_CTX(body->ts); + return php_stream_read(s, data, len * n); + } else abort(); + } + return 0; + } + + #if PHP_HTTP_CURL_VERSION(7,32,0) + static int php_http_curle_xferinfo_callback(void *ctx, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) + #else + static int php_http_curle_progress_callback(void *ctx, double dltotal, double dlnow, double ultotal, double ulnow) + #endif + { + php_http_client_curl_handler_t *h = ctx; + zend_bool update = 0; + + if (h->progress.dl.total != dltotal + || h->progress.dl.now != dlnow + || h->progress.ul.total != ultotal + || h->progress.ul.now != ulnow + ) { + update = 1; + + h->progress.dl.total = dltotal; + h->progress.dl.now = dlnow; + h->progress.ul.total = ultotal; + h->progress.ul.now = ulnow; + } + + if (update && h->client->callback.progress.func) { + h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress); + } + + return 0; + } + + static int php_http_curle_seek_callback(void *userdata, curl_off_t offset, int origin) + { + php_http_message_body_t *body = userdata; - TSRMLS_FETCH_FROM_CTX(body->ts); + + if (!body) { + return 1; + } + if (0 == php_stream_seek(php_http_message_body_stream(body), offset, origin)) { + return 0; + } + return 2; + } + + static int php_http_curle_raw_callback(CURL *ch, curl_infotype type, char *data, size_t length, void *ctx) + { + php_http_client_curl_handler_t *h = ctx; + + /* catch progress */ + switch (type) { + case CURLINFO_TEXT: + if (data[0] == '-') { + } else if (php_memnstr(data, ZEND_STRL("Adding handle:"), data + length)) { + h->progress.info = "setup"; + } else if (php_memnstr(data, ZEND_STRL("addHandle"), data + length)) { + h->progress.info = "setup"; + } else if (php_memnstr(data, ZEND_STRL("About to connect"), data + length)) { + h->progress.info = "resolve"; + } else if (php_memnstr(data, ZEND_STRL("Trying"), data + length)) { + h->progress.info = "connect"; + } else if (php_memnstr(data, ZEND_STRL("Found bundle for host"), data + length)) { + h->progress.info = "connect"; + } else if (php_memnstr(data, ZEND_STRL("Connected"), data + length)) { + h->progress.info = "connected"; + } else if (php_memnstr(data, ZEND_STRL("Re-using existing connection!"), data + length)) { + h->progress.info = "connected"; + } else if (php_memnstr(data, ZEND_STRL("blacklisted"), data + length)) { + h->progress.info = "blacklist check"; + } else if (php_memnstr(data, ZEND_STRL("SSL"), data + length)) { + h->progress.info = "ssl negotiation"; + } else if (php_memnstr(data, ZEND_STRL("upload"), data + length)) { + h->progress.info = "uploaded"; + } else if (php_memnstr(data, ZEND_STRL("left intact"), data + length)) { + h->progress.info = "not disconnected"; + } else if (php_memnstr(data, ZEND_STRL("closed"), data + length)) { + h->progress.info = "disconnected"; + } else if (php_memnstr(data, ZEND_STRL("Issue another request"), data + length)) { + h->progress.info = "redirect"; + } else if (php_memnstr(data, ZEND_STRL("Operation timed out"), data + length)) { + h->progress.info = "timeout"; + } else { + #if 0 + h->progress.info = data; + data[length - 1] = '\0'; + #endif + } + if (h->client->callback.progress.func) { + h->client->callback.progress.func(h->client->callback.progress.arg, h->client, &h->queue, &h->progress); + } + break; + case CURLINFO_HEADER_OUT: + case CURLINFO_DATA_OUT: + case CURLINFO_SSL_DATA_OUT: + h->progress.info = "send"; + break; + case CURLINFO_HEADER_IN: + case CURLINFO_DATA_IN: + case CURLINFO_SSL_DATA_IN: + h->progress.info = "receive"; + break; + default: + break; + } + + #if 0 + /* debug */ + _dpf(type, data, length); + #endif + + return 0; + } + + static int php_http_curle_header_callback(char *data, size_t n, size_t l, void *arg) + { + php_http_client_curl_handler_t *h = arg; + + return php_http_buffer_append(&h->response.headers, data, n * l); + } + + static int php_http_curle_body_callback(char *data, size_t n, size_t l, void *arg) + { + php_http_client_curl_handler_t *h = arg; + + return php_http_message_body_append(h->response.body, data, n*l); + } + + static ZEND_RESULT_CODE php_http_curle_get_info(CURL *ch, HashTable *info) + { - char *c; - long l; - double d; - struct curl_slist *s, *p; - zval *subarray, array; - INIT_PZVAL_ARRAY(&array, info); ++ char *c = NULL; ++ long l = 0; ++ double d = 0; ++ struct curl_slist *s = NULL, *p = NULL; ++ zval tmp = {{0}}; + + /* BEGIN::CURLINFO */ + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_EFFECTIVE_URL, &c)) { - add_assoc_string_ex(&array, "effective_url", sizeof("effective_url"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "effective_url", lenof("effective_url"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) { - add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "response_code", lenof("response_code"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) { - add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "total_time", lenof("total_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) { - add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "namelookup_time", lenof("namelookup_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) { - add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "connect_time", lenof("connect_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) { - add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "pretransfer_time", lenof("pretransfer_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) { - add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "size_upload", lenof("size_upload"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) { - add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "size_download", lenof("size_download"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) { - add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "speed_download", lenof("speed_download"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) { - add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "speed_upload", lenof("speed_upload"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) { - add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "header_size", lenof("header_size"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) { - add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "request_size", lenof("request_size"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) { - add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "ssl_verifyresult", lenof("ssl_verifyresult"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) { - add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "filetime", lenof("filetime"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) { - add_assoc_double_ex(&array, "content_length_download", sizeof("content_length_download"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "content_length_download", lenof("content_length_download"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_LENGTH_UPLOAD, &d)) { - add_assoc_double_ex(&array, "content_length_upload", sizeof("content_length_upload"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "content_length_upload", lenof("content_length_upload"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) { - add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "starttransfer_time", lenof("starttransfer_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) { - add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "content_type", lenof("content_type"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) { - add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "redirect_time", lenof("redirect_time"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) { - add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "redirect_count", lenof("redirect_count"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) { - add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "connect_code", lenof("connect_code"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) { - add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "httpauth_avail", lenof("httpauth_avail"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) { - add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "proxyauth_avail", lenof("proxyauth_avail"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) { - add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "os_errno", lenof("os_errno"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) { - add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "num_connects", lenof("num_connects"), &tmp); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) { - MAKE_STD_ZVAL(subarray); - array_init(subarray); ++ array_init(&tmp); + for (p = s; p; p = p->next) { + if (p->data) { - add_next_index_string(subarray, p->data, 1); ++ add_next_index_string(&tmp, p->data); + } + } - add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray); ++ zend_hash_str_update(info, "ssl_engines", lenof("ssl_engines"), &tmp); + curl_slist_free_all(s); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_URL, &c)) { - add_assoc_string_ex(&array, "redirect_url", sizeof("redirect_url"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "redirect_url", lenof("redirect_url"), &tmp); + } + #if PHP_HTTP_CURL_VERSION(7,19,0) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_IP, &c)) { - add_assoc_string_ex(&array, "primary_ip", sizeof("primary_ip"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "primary_ip", lenof("primary_ip"), &tmp); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,19,0) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_APPCONNECT_TIME, &d)) { - add_assoc_double_ex(&array, "appconnect_time", sizeof("appconnect_time"), d); ++ ZVAL_DOUBLE(&tmp, d); ++ zend_hash_str_update(info, "appconnect_time", lenof("appconnect_time"), &tmp); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,19,4) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONDITION_UNMET, &l)) { - add_assoc_long_ex(&array, "condition_unmet", sizeof("condition_unmet"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "condition_unmet", lenof("condition_unmet"), &tmp); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,21,0) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRIMARY_PORT, &l)) { - add_assoc_long_ex(&array, "primary_port", sizeof("primary_port"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "primary_port", lenof("primary_port"), &tmp); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,21,0) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_IP, &c)) { - add_assoc_string_ex(&array, "local_ip", sizeof("local_ip"), c ? c : "", 1); ++ ZVAL_STRING(&tmp, STR_PTR(c)); ++ zend_hash_str_update(info, "local_ip", lenof("local_ip"), &tmp); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,21,0) + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_LOCAL_PORT, &l)) { - add_assoc_long_ex(&array, "local_port", sizeof("local_port"), l); ++ ZVAL_LONG(&tmp, l); ++ zend_hash_str_update(info, "local_port", lenof("local_port"), &tmp); + } + #endif + + /* END::CURLINFO */ + + #if PHP_HTTP_CURL_VERSION(7,34,0) + { - zval *ti_array; ++ zval ti_array, subarray; + struct curl_tlssessioninfo *ti; + + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) { - const char *backend; ++ char *backend; + - MAKE_STD_ZVAL(subarray); - ZVAL_NULL(subarray); - MAKE_STD_ZVAL(ti_array); - array_init(ti_array); ++ ZVAL_NULL(&subarray); ++ array_init(&ti_array); + + switch (ti->backend) { + case CURLSSLBACKEND_NONE: + backend = "none"; + break; + case CURLSSLBACKEND_OPENSSL: + backend = "openssl"; + #ifdef PHP_HTTP_HAVE_OPENSSL + { + SSL_CTX *ctx = ti->internals; + - array_init(subarray); - add_assoc_long_ex(subarray, ZEND_STRS("number"), SSL_CTX_sess_number(ctx)); - add_assoc_long_ex(subarray, ZEND_STRS("connect"), SSL_CTX_sess_connect(ctx)); - add_assoc_long_ex(subarray, ZEND_STRS("connect_good"), SSL_CTX_sess_connect_good(ctx)); - add_assoc_long_ex(subarray, ZEND_STRS("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx)); - add_assoc_long_ex(subarray, ZEND_STRS("hits"), SSL_CTX_sess_hits(ctx)); - add_assoc_long_ex(subarray, ZEND_STRS("cache_full"), SSL_CTX_sess_cache_full(ctx)); ++ array_init(&subarray); ++ add_assoc_long_ex(&subarray, ZEND_STRL("number"), SSL_CTX_sess_number(ctx)); ++ add_assoc_long_ex(&subarray, ZEND_STRL("connect"), SSL_CTX_sess_connect(ctx)); ++ add_assoc_long_ex(&subarray, ZEND_STRL("connect_good"), SSL_CTX_sess_connect_good(ctx)); ++ add_assoc_long_ex(&subarray, ZEND_STRL("connect_renegotiate"), SSL_CTX_sess_connect_renegotiate(ctx)); ++ add_assoc_long_ex(&subarray, ZEND_STRL("hits"), SSL_CTX_sess_hits(ctx)); ++ add_assoc_long_ex(&subarray, ZEND_STRL("cache_full"), SSL_CTX_sess_cache_full(ctx)); + } + #endif + break; + case CURLSSLBACKEND_GNUTLS: + backend = "gnutls"; + #ifdef PHP_HTTP_HAVE_GNUTLS + { + gnutls_session_t sess = ti->internals; + char *desc; + - array_init(subarray); ++ array_init(&subarray); + if ((desc = gnutls_session_get_desc(sess))) { - add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1); ++ add_assoc_string_ex(&subarray, ZEND_STRL("desc"), desc); + gnutls_free(desc); + } - add_assoc_bool_ex(subarray, ZEND_STRS("resumed"), gnutls_session_is_resumed(sess)); ++ add_assoc_bool_ex(&subarray, ZEND_STRL("resumed"), gnutls_session_is_resumed(sess)); + } + #endif + break; + case CURLSSLBACKEND_NSS: + backend = "nss"; + break; + #if !PHP_HTTP_CURL_VERSION(7,39,0) + case CURLSSLBACKEND_QSOSSL: + backend = "qsossl"; + break; + #else + case CURLSSLBACKEND_GSKIT: + backend = "gskit"; + break; + #endif + case CURLSSLBACKEND_POLARSSL: + backend = "polarssl"; + break; + case CURLSSLBACKEND_CYASSL: + backend = "cyassl"; + break; + case CURLSSLBACKEND_SCHANNEL: + backend = "schannel"; + break; + case CURLSSLBACKEND_DARWINSSL: + backend = "darwinssl"; + break; + default: + backend = "unknown"; + } - add_assoc_string_ex(ti_array, ZEND_STRS("backend"), estrdup(backend), 0); - add_assoc_zval_ex(ti_array, ZEND_STRS("internals"), subarray); - add_assoc_zval_ex(&array, "tls_session", sizeof("tls_session"), ti_array); ++ add_assoc_string_ex(&ti_array, ZEND_STRL("backend"), backend); ++ add_assoc_zval_ex(&ti_array, ZEND_STRL("internals"), &subarray); ++ zend_hash_str_update(info, "tls_session", lenof("tls_session"), &ti_array); + } + } + #endif + + #if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT)) + { + int i; - zval *ci_array; ++ zval ci_array, subarray; + struct curl_certinfo *ci; + char *colon, *keyname; + + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CERTINFO, &ci)) { - MAKE_STD_ZVAL(ci_array); - array_init(ci_array); ++ array_init(&ci_array); + + for (i = 0; i < ci->num_of_certs; ++i) { + s = ci->certinfo[i]; + - MAKE_STD_ZVAL(subarray); - array_init(subarray); ++ array_init(&subarray); + for (p = s; p; p = p->next) { + if (p->data) { + if ((colon = strchr(p->data, ':'))) { + keyname = estrndup(p->data, colon - p->data); - add_assoc_string_ex(subarray, keyname, colon - p->data + 1, colon + 1, 1); ++ add_assoc_string_ex(&subarray, keyname, colon - p->data, colon + 1); + efree(keyname); + } else { - add_next_index_string(subarray, p->data, 1); ++ add_next_index_string(&subarray, p->data); + } + } + } - add_next_index_zval(ci_array, subarray); ++ add_next_index_zval(&ci_array, &subarray); + } - add_assoc_zval_ex(&array, "certinfo", sizeof("certinfo"), ci_array); ++ zend_hash_str_update(info, "certinfo", lenof("certinfo"), &ci_array); + } + } + #endif + { + php_http_curle_storage_t *st = php_http_curle_get_storage(ch); + - add_assoc_long_ex(&array, "curlcode", sizeof("curlcode"), st->errorcode); - add_assoc_string_ex(&array, "error", sizeof("error"), st->errorbuffer, 1); ++ ZVAL_LONG(&tmp, st->errorcode); ++ zend_hash_str_update(info, "curlcode", lenof("curlcode"), &tmp); ++ ZVAL_STRING(&tmp, st->errorbuffer); ++ zend_hash_str_update(info, "error", lenof("error"), &tmp); + } + + return SUCCESS; + } + + static int compare_queue(php_http_client_enqueue_t *e, void *handle) + { + return handle == ((php_http_client_curl_handler_t *) e->opaque)->handle; + } + -static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h TSRMLS_DC) ++static php_http_message_t *php_http_curlm_responseparser(php_http_client_curl_handler_t *h) + { + php_http_message_t *response; + php_http_header_parser_t parser; + zval *zh; + - response = php_http_message_init(NULL, 0, h->response.body TSRMLS_CC); - php_http_header_parser_init(&parser TSRMLS_CC); ++ response = php_http_message_init(NULL, 0, h->response.body); ++ php_http_header_parser_init(&parser); + while (h->response.headers.used) { + php_http_header_parser_state_t st = php_http_header_parser_parse(&parser, + &h->response.headers, PHP_HTTP_HEADER_PARSER_CLEANUP, &response->hdrs, + (php_http_info_callback_t) php_http_message_info_callback, (void *) &response); + if (PHP_HTTP_HEADER_PARSER_STATE_FAILURE == st) { + break; + } + } + php_http_header_parser_dtor(&parser); + + /* move body to right message */ + if (response->body != h->response.body) { + php_http_message_t *ptr = response; + + while (ptr->parent) { + ptr = ptr->parent; + } + php_http_message_body_free(&response->body); + response->body = ptr->body; + ptr->body = NULL; + } + php_http_message_body_addref(h->response.body); + + /* let's update the response headers */ - if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length"), 1))) { - zend_hash_update(&response->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), &zh, sizeof(zval *), NULL); ++ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Length")))) { ++ Z_TRY_ADDREF_P(zh); ++ zend_hash_str_update(&response->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), zh); + } - if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding"), 0))) { - zend_hash_update(&response->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &zh, sizeof(zval *), NULL); - zend_hash_del(&response->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding")); ++ if ((zh = php_http_message_header(response, ZEND_STRL("Transfer-Encoding")))) { ++ Z_TRY_ADDREF_P(zh); ++ zend_hash_str_update(&response->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), zh); ++ zend_hash_str_del(&response->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding")); + } - if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range"), 0))) { - zend_hash_update(&response->hdrs, "X-Original-Content-Range", sizeof("X-Original-Content-Range"), &zh, sizeof(zval *), NULL); - zend_hash_del(&response->hdrs, "Content-Range", sizeof("Content-Range")); ++ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Range")))) { ++ Z_TRY_ADDREF_P(zh); ++ zend_hash_str_update(&response->hdrs, "X-Original-Content-Range", lenof("X-Original-Content-Range"), zh); ++ zend_hash_str_del(&response->hdrs, "Content-Range", lenof("Content-Range")); + } - if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding"), 0))) { - zend_hash_update(&response->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &zh, sizeof(zval *), NULL); - zend_hash_del(&response->hdrs, "Content-Encoding", sizeof("Content-Encoding")); ++ if ((zh = php_http_message_header(response, ZEND_STRL("Content-Encoding")))) { ++ Z_TRY_ADDREF_P(zh); ++ zend_hash_str_update(&response->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), zh); ++ zend_hash_str_del(&response->hdrs, "Content-Encoding", lenof("Content-Encoding")); + } + php_http_message_update_headers(response); + + return response; + } + + static void php_http_curlm_responsehandler(php_http_client_t *context) + { + int err_count = 0, remaining = 0; + php_http_curle_storage_t *st, *err = NULL; + php_http_client_enqueue_t *enqueue; + php_http_client_curl_t *curl = context->ctx; - TSRMLS_FETCH_FROM_CTX(context->ts); + + do { + CURLMsg *msg = curl_multi_info_read(curl->handle, &remaining); + + if (msg && CURLMSG_DONE == msg->msg) { + if (CURLE_OK != msg->data.result) { + st = php_http_curle_get_storage(msg->easy_handle); + st->errorcode = msg->data.result; + + /* defer the warnings/exceptions, so the callback is still called for this request */ + if (!err) { + err = ecalloc(remaining + 1, sizeof(*err)); + } + memcpy(&err[err_count], st, sizeof(*st)); + if (st->url) { + err[err_count].url = estrdup(st->url); + } + err_count++; + } + + if ((enqueue = php_http_client_enqueued(context, msg->easy_handle, compare_queue))) { + php_http_client_curl_handler_t *handler = enqueue->opaque; - php_http_message_t *response = php_http_curlm_responseparser(handler TSRMLS_CC); ++ php_http_message_t *response = php_http_curlm_responseparser(handler); + + if (response) { + context->callback.response.func(context->callback.response.arg, context, &handler->queue, &response); + php_http_message_free(&response); + } + } + } + } while (remaining); + + if (err_count) { + int i = 0; + + do { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url)); ++ php_error_docref(NULL, E_WARNING, "%s; %s (%s)", curl_easy_strerror(err[i].errorcode), err[i].errorbuffer, STR_PTR(err[i].url)); + if (err[i].url) { + efree(err[i].url); + } + } while (++i < err_count); + + efree(err); + } + } + + #if PHP_HTTP_HAVE_EVENT + + typedef struct php_http_curlm_event { + struct event evnt; + php_http_client_t *context; + } php_http_curlm_event_t; + + static inline int etoca(short action) { + switch (action & (EV_READ|EV_WRITE)) { + case EV_READ: + return CURL_CSELECT_IN; + break; + case EV_WRITE: + return CURL_CSELECT_OUT; + break; + case EV_READ|EV_WRITE: + return CURL_CSELECT_IN|CURL_CSELECT_OUT; + break; + default: + return 0; + } + } + + static void php_http_curlm_timeout_callback(int socket, short action, void *event_data) + { + php_http_client_t *context = event_data; + php_http_client_curl_t *curl = context->ctx; + + #if DBG_EVENTS + fprintf(stderr, "T"); + #endif + if (curl->useevents) { + CURLMcode rc; - TSRMLS_FETCH_FROM_CTX(context->ts); + + /* ignore and use -1,0 on timeout */ + (void) socket; + (void) action; + + while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, CURL_SOCKET_TIMEOUT, 0, &curl->unfinished))); + + if (CURLM_OK != rc) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc)); ++ php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc)); + } + + php_http_curlm_responsehandler(context); + } + } + + static void php_http_curlm_event_callback(int socket, short action, void *event_data) + { + php_http_client_t *context = event_data; + php_http_client_curl_t *curl = context->ctx; + + #if DBG_EVENTS + fprintf(stderr, "E"); + #endif + if (curl->useevents) { + CURLMcode rc = CURLM_OK; - TSRMLS_FETCH_FROM_CTX(context->ts); + + while (CURLM_CALL_MULTI_PERFORM == (rc = curl_multi_socket_action(curl->handle, socket, etoca(action), &curl->unfinished))); + + if (CURLM_OK != rc) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", curl_multi_strerror(rc)); ++ php_error_docref(NULL, E_WARNING, "%s", curl_multi_strerror(rc)); + } + + php_http_curlm_responsehandler(context); + + /* remove timeout if there are no transfers left */ + if (!curl->unfinished && event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) { + event_del(curl->timeout); + } + } + } + + static int php_http_curlm_socket_callback(CURL *easy, curl_socket_t sock, int action, void *socket_data, void *assign_data) + { + php_http_client_t *context = socket_data; + php_http_client_curl_t *curl = context->ctx; + + #if DBG_EVENTS + fprintf(stderr, "S"); + #endif + if (curl->useevents) { + int events = EV_PERSIST; + php_http_curlm_event_t *ev = assign_data; - TSRMLS_FETCH_FROM_CTX(context->ts); + + if (!ev) { + ev = ecalloc(1, sizeof(php_http_curlm_event_t)); + ev->context = context; + curl_multi_assign(curl->handle, sock, ev); + } else { + event_del(&ev->evnt); + } + + switch (action) { + case CURL_POLL_IN: + events |= EV_READ; + break; + case CURL_POLL_OUT: + events |= EV_WRITE; + break; + case CURL_POLL_INOUT: + events |= EV_READ|EV_WRITE; + break; + + case CURL_POLL_REMOVE: + efree(ev); + /* no break */ + case CURL_POLL_NONE: + return 0; + + default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown socket action %d", action); ++ php_error_docref(NULL, E_WARNING, "Unknown socket action %d", action); + return -1; + } + + event_assign(&ev->evnt, curl->evbase, sock, events, php_http_curlm_event_callback, context); + event_add(&ev->evnt, NULL); + } + + return 0; + } + + static void php_http_curlm_timer_callback(CURLM *multi, long timeout_ms, void *timer_data) + { + php_http_client_t *context = timer_data; + php_http_client_curl_t *curl = context->ctx; + + #if DBG_EVENTS + fprintf(stderr, "\ntimer <- timeout_ms: %ld\n", timeout_ms); + #endif + if (curl->useevents) { + + if (timeout_ms < 0) { + php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, context); + } else if (timeout_ms > 0 || !event_initialized(curl->timeout) || !event_pending(curl->timeout, EV_TIMEOUT, NULL)) { + struct timeval timeout; + + if (!event_initialized(curl->timeout)) { + event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, context); + } + + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + + event_add(curl->timeout, &timeout); + } + } + } + + #endif /* HAVE_EVENT */ + + /* curl options */ + + static php_http_options_t php_http_curle_options, php_http_curlm_options; + + #define PHP_HTTP_CURLE_OPTION_CHECK_STRLEN 0x0001 + #define PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR 0x0002 + #define PHP_HTTP_CURLE_OPTION_TRANSFORM_MS 0x0004 + + static ZEND_RESULT_CODE php_http_curle_option_set_ssl_verifyhost(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + - if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_BVAL_P(val) ? 2 : 0)) { ++ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, Z_TYPE_P(val) == IS_TRUE ? 2 : 0)) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_cookiestore(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle); + + if (storage->cookiestore) { + pefree(storage->cookiestore, 1); + } - if (val && Z_STRLEN_P(val)) { ++ if (val && Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val)) { + storage->cookiestore = pestrndup(Z_STRVAL_P(val), Z_STRLEN_P(val), 1); + } else { + storage->cookiestore = NULL; + } + if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEFILE, storage->cookiestore) + || CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIEJAR, storage->cookiestore) + ) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_cookies(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (val && Z_TYPE_P(val) != IS_NULL) { ++ HashTable *ht = HASH_OF(val); ++ + if (curl->options.encode_cookies) { - if (SUCCESS == php_http_url_encode_hash_ex(HASH_OF(val), &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0 TSRMLS_CC)) { ++ if (SUCCESS == php_http_url_encode_hash_ex(ht, &curl->options.cookies, ZEND_STRL(";"), ZEND_STRL("="), NULL, 0)) { + php_http_buffer_fix(&curl->options.cookies); + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) { + return FAILURE; + } + } else { + return FAILURE; + } + } else { - HashPosition pos; - php_http_array_hashkey_t cookie_key = php_http_array_hashkey_init(0); - zval **cookie_val; ++ php_http_arrkey_t cookie_key; ++ zval *cookie_val; + - FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) { - zval *zv = php_http_ztyp(IS_STRING, *cookie_val); ++ ZEND_HASH_FOREACH_KEY_VAL(ht, cookie_key.h, cookie_key.key, cookie_val) ++ { ++ zend_string *zs = zval_get_string(cookie_val); + - php_http_array_hashkey_stringify(&cookie_key); - php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.str, Z_STRVAL_P(zv)); - php_http_array_hashkey_stringfree(&cookie_key); ++ php_http_arrkey_stringify(&cookie_key, NULL); ++ php_http_buffer_appendf(&curl->options.cookies, "%s=%s; ", cookie_key.key->val, zs->val); ++ php_http_arrkey_dtor(&cookie_key); + - zval_ptr_dtor(&zv); ++ zend_string_release(zs); + } ++ ZEND_HASH_FOREACH_END(); + + php_http_buffer_fix(&curl->options.cookies); + if (curl->options.cookies.used) { + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, curl->options.cookies.data)) { + return FAILURE; + } + } + } + } else { + php_http_buffer_reset(&curl->options.cookies); + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_COOKIE, NULL)) { + return FAILURE; + } + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_encodecookies(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + - curl->options.encode_cookies = Z_BVAL_P(val); ++ curl->options.encode_cookies = Z_TYPE_P(val) == IS_TRUE; + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_lastmodified(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (Z_LVAL_P(val)) { + if (Z_LVAL_P(val) > 0) { + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, Z_LVAL_P(val))) { + return FAILURE; + } + } else { - if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time(TSRMLS_C) + Z_LVAL_P(val))) { ++ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, (long) sapi_get_request_time() + Z_LVAL_P(val))) { + return FAILURE; + } + } + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, (long) (curl->options.range_request ? CURL_TIMECOND_IFUNMODSINCE : CURL_TIMECOND_IFMODSINCE))) { + return FAILURE; + } + } else { + if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0) + || CURLE_OK != curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0) + ) { + return FAILURE; + } + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_compress(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + + #if !PHP_HTTP_CURL_VERSION(7,21,6) + # define CURLOPT_ACCEPT_ENCODING CURLOPT_ENCODING + #endif - if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_BVAL_P(val) ? "" : NULL)) { ++ if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_ACCEPT_ENCODING, Z_TYPE_P(val) == IS_TRUE ? "" : NULL)) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_etag(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + php_http_buffer_t header; + - if (Z_STRLEN_P(val)) { ++ if (val && Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val)) { + zend_bool is_quoted = !((Z_STRVAL_P(val)[0] != '"') || (Z_STRVAL_P(val)[Z_STRLEN_P(val)-1] != '"')); + php_http_buffer_init(&header); + php_http_buffer_appendf(&header, is_quoted?"%s: %s":"%s: \"%s\"", curl->options.range_request?"If-Match":"If-None-Match", Z_STRVAL_P(val)); + php_http_buffer_fix(&header); + curl->options.headers = curl_slist_append(curl->options.headers, header.data); + php_http_buffer_dtor(&header); + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_range(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + php_http_buffer_reset(&curl->options.ranges); + + if (val && Z_TYPE_P(val) != IS_NULL) { - HashPosition pos; - zval **rr, **rb, **re; - - FOREACH_VAL(pos, val, rr) { - if (Z_TYPE_PP(rr) == IS_ARRAY) { - if (2 == php_http_array_list(Z_ARRVAL_PP(rr) TSRMLS_CC, 2, &rb, &re)) { - if ( ((Z_TYPE_PP(rb) == IS_LONG) || ((Z_TYPE_PP(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(rb), Z_STRLEN_PP(rb), NULL, NULL, 1))) && - ((Z_TYPE_PP(re) == IS_LONG) || ((Z_TYPE_PP(re) == IS_STRING) && is_numeric_string(Z_STRVAL_PP(re), Z_STRLEN_PP(re), NULL, NULL, 1)))) { - zval *rbl = php_http_ztyp(IS_LONG, *rb); - zval *rel = php_http_ztyp(IS_LONG, *re); - - if ((Z_LVAL_P(rbl) >= 0) && (Z_LVAL_P(rel) >= 0)) { - php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", Z_LVAL_P(rbl), Z_LVAL_P(rel)); ++ zval *rr, *rb, *re; ++ zend_long rbl, rel; ++ HashTable *ht = HASH_OF(val); ++ ++ ZEND_HASH_FOREACH_VAL(ht, rr) ++ { ++ if (Z_TYPE_P(rr) == IS_ARRAY) { ++ if (2 == php_http_array_list(Z_ARRVAL_P(rr), 2, &rb, &re)) { ++ if ( ((Z_TYPE_P(rb) == IS_LONG) || ((Z_TYPE_P(rb) == IS_STRING) && is_numeric_string(Z_STRVAL_P(rb), Z_STRLEN_P(rb), &rbl, NULL, 1))) && ++ ((Z_TYPE_P(re) == IS_LONG) || ((Z_TYPE_P(re) == IS_STRING) && is_numeric_string(Z_STRVAL_P(re), Z_STRLEN_P(re), &rel, NULL, 1)))) { ++ if ((rbl >= 0) && (rel >= 0)) { ++ php_http_buffer_appendf(&curl->options.ranges, "%ld-%ld,", rbl, rel); + } - zval_ptr_dtor(&rbl); - zval_ptr_dtor(&rel); + } + + } + } + } ++ ZEND_HASH_FOREACH_END(); + + if (curl->options.ranges.used) { + curl->options.range_request = 1; + /* ditch last comma */ + curl->options.ranges.data[curl->options.ranges.used - 1] = '\0'; + } + } + + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RANGE, curl->options.ranges.data)) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_resume(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + + if (Z_LVAL_P(val) > 0) { + curl->options.range_request = 1; + } + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESUME_FROM, Z_LVAL_P(val))) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_retrydelay(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + + curl->options.retry.delay = Z_DVAL_P(val); + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_retrycount(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + + curl->options.retry.count = Z_LVAL_P(val); + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_redirect(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + + if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, Z_LVAL_P(val) ? 1L : 0L) + || CURLE_OK != curl_easy_setopt(ch, CURLOPT_MAXREDIRS, curl->options.redirects = Z_LVAL_P(val)) + ) { + return FAILURE; + } + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curle_option_set_portrange(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + long localport = 0, localportrange = 0; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (val && Z_TYPE_P(val) != IS_NULL) { - zval **z_port_start, *zps_copy = NULL, **z_port_end, *zpe_copy = NULL; ++ zval *zps, *zpe; + - switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) { ++ switch (php_http_array_list(Z_ARRVAL_P(val), 2, &zps, &zpe)) { + case 2: - zps_copy = php_http_ztyp(IS_LONG, *z_port_start); - zpe_copy = php_http_ztyp(IS_LONG, *z_port_end); - localportrange = labs(Z_LVAL_P(zps_copy)-Z_LVAL_P(zpe_copy))+1L; ++ localportrange = labs(zval_get_long(zps)-zval_get_long(zpe))+1L; + /* no break */ + case 1: - if (!zps_copy) { - zps_copy = php_http_ztyp(IS_LONG, *z_port_start); - } - localport = (zpe_copy && Z_LVAL_P(zpe_copy) > 0) ? MIN(Z_LVAL_P(zps_copy), Z_LVAL_P(zpe_copy)) : Z_LVAL_P(zps_copy); - zval_ptr_dtor(&zps_copy); - if (zpe_copy) { - zval_ptr_dtor(&zpe_copy); - } ++ localport = (zval_get_long(zpe) > 0) ? MIN(zval_get_long(zps), zval_get_long(zpe)) : zval_get_long(zps); + break; + default: + break; + } + } + if ( CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORT, localport) + || CURLE_OK != curl_easy_setopt(ch, CURLOPT_LOCALPORTRANGE, localportrange) + ) { + return FAILURE; + } + return SUCCESS; + } + + #if PHP_HTTP_CURL_VERSION(7,37,0) + static ZEND_RESULT_CODE php_http_curle_option_set_proxyheader(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (val && Z_TYPE_P(val) != IS_NULL) { - php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0); - zval **header_val, *header_cpy; - HashPosition pos; ++ php_http_arrkey_t header_key; ++ zval *header_val; + php_http_buffer_t header; + + php_http_buffer_init(&header); - FOREACH_KEYVAL(pos, val, header_key, header_val) { - if (header_key.type == HASH_KEY_IS_STRING) { - header_cpy = php_http_ztyp(IS_STRING, *header_val); - php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy)); ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(val), header_key.h, header_key.key, header_val) ++ { ++ if (header_key.key) { ++ zend_string *zs = zval_get_string(header_val); ++ ++ php_http_buffer_appendf(&header, "%s: %s", header_key.key->val, zs->val); ++ zend_string_release(zs); ++ + php_http_buffer_fix(&header); + curl->options.proxyheaders = curl_slist_append(curl->options.proxyheaders, header.data); + php_http_buffer_reset(&header); + - zval_ptr_dtor(&header_cpy); + } + } ++ ZEND_HASH_FOREACH_END(); + php_http_buffer_dtor(&header); + } + if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, curl->options.proxyheaders)) { + return FAILURE; + } + if (CURLE_OK != curl_easy_setopt(curl->handle, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE)) { + curl_easy_setopt(curl->handle, CURLOPT_PROXYHEADER, NULL); + return FAILURE; + } + return SUCCESS; + } + #endif + + #if PHP_HTTP_CURL_VERSION(7,21,3) + static ZEND_RESULT_CODE php_http_curle_option_set_resolve(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (val && Z_TYPE_P(val) != IS_NULL) { - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - HashPosition pos; - zval **data; ++ HashTable *ht = HASH_OF(val); ++ zval *data; + - FOREACH_KEYVAL(pos, val, key, data) { - zval *cpy = php_http_ztyp(IS_STRING, *data); - curl->options.resolve = curl_slist_append(curl->options.resolve, Z_STRVAL_P(cpy)); - zval_ptr_dtor(&cpy); ++ ZEND_HASH_FOREACH_VAL(ht, data) ++ { ++ zend_string *zs = zval_get_string(data); ++ curl->options.resolve = curl_slist_append(curl->options.resolve, zs->val); ++ zend_string_release(zs); + } ++ ZEND_HASH_FOREACH_END(); + + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, curl->options.resolve)) { + return FAILURE; + } + } else { + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_RESOLVE, NULL)) { + return FAILURE; + } + } + return SUCCESS; + } + #endif + + #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) + static ZEND_RESULT_CODE php_http_curle_option_set_ssl_tlsauthtype(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + + if (val && Z_LVAL_P(val)) { + switch (Z_LVAL_P(val)) { + case CURL_TLSAUTH_SRP: + if (CURLE_OK == curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_SRP)) { + return SUCCESS; + } + /* no break */ + default: + return FAILURE; + } + } + if (CURLE_OK != curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, PHP_HTTP_CURL_TLSAUTH_DEF)) { + return FAILURE; + } + return SUCCESS; + } + #endif + -static void php_http_curle_options_init(php_http_options_t *registry TSRMLS_DC) ++static void php_http_curle_options_init(php_http_options_t *registry) + { + php_http_option_t *opt; + + /* url options */ + #if PHP_HTTP_CURL_VERSION(7,42,0) + php_http_option_register(registry, ZEND_STRL("path_as_is"), CURLOPT_PATH_AS_IS, IS_BOOL); + #endif + + /* proxy */ + if ((opt = php_http_option_register(registry, ZEND_STRL("proxyhost"), CURLOPT_PROXY, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + php_http_option_register(registry, ZEND_STRL("proxytype"), CURLOPT_PROXYTYPE, IS_LONG); + php_http_option_register(registry, ZEND_STRL("proxyport"), CURLOPT_PROXYPORT, IS_LONG); + if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauth"), CURLOPT_PROXYUSERPWD, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("proxyauthtype"), CURLOPT_PROXYAUTH, IS_LONG))) { + Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE; + } - php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("proxytunnel"), CURLOPT_HTTPPROXYTUNNEL, _IS_BOOL); + #if PHP_HTTP_CURL_VERSION(7,19,4) + php_http_option_register(registry, ZEND_STRL("noproxy"), CURLOPT_NOPROXY, IS_STRING); + #endif + + #if PHP_HTTP_CURL_VERSION(7,37,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("proxyheader"), CURLOPT_PROXYHEADER, IS_ARRAY))) { + opt->setter = php_http_curle_option_set_proxyheader; + } + #endif + #if PHP_HTTP_CURL_VERSION(7,43,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("proxy_service_name"), CURLOPT_PROXY_SERVICE_NAME, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + #endif + + #if PHP_HTTP_CURL_VERSION(7,40,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("unix_socket_path"), CURLOPT_UNIX_SOCKET_PATH, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + #endif + + /* dns */ + if ((opt = php_http_option_register(registry, ZEND_STRL("dns_cache_timeout"), CURLOPT_DNS_CACHE_TIMEOUT, IS_LONG))) { + Z_LVAL(opt->defval) = 60; + } + php_http_option_register(registry, ZEND_STRL("ipresolve"), CURLOPT_IPRESOLVE, IS_LONG); + #if PHP_HTTP_CURL_VERSION(7,21,3) + if ((opt = php_http_option_register(registry, ZEND_STRL("resolve"), CURLOPT_RESOLVE, IS_ARRAY))) { + opt->setter = php_http_curle_option_set_resolve; + } + #endif + #if PHP_HTTP_HAVE_ARES + # if PHP_HTTP_CURL_VERSION(7,24,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("dns_servers"), CURLOPT_DNS_SERVERS, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + # endif + # if PHP_HTTP_CURL_VERSION(7,33,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("dns_interface"), CURLOPT_DNS_INTERFACE, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip4"), CURLOPT_DNS_LOCAL_IP4, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("dns_local_ip6"), CURLOPT_DNS_LOCAL_IP6, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + # endif + #endif + + /* limits */ + php_http_option_register(registry, ZEND_STRL("low_speed_limit"), CURLOPT_LOW_SPEED_LIMIT, IS_LONG); + php_http_option_register(registry, ZEND_STRL("low_speed_time"), CURLOPT_LOW_SPEED_TIME, IS_LONG); + + /* LSF weirdance + php_http_option_register(registry, ZEND_STRL("max_send_speed"), CURLOPT_MAX_SEND_SPEED_LARGE, IS_LONG); + php_http_option_register(registry, ZEND_STRL("max_recv_speed"), CURLOPT_MAX_RECV_SPEED_LARGE, IS_LONG); + */ + + /* connection handling */ + /* crashes + if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLOPT_MAXCONNECTS, IS_LONG))) { + Z_LVAL(opt->defval) = 5; + } + */ - php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, IS_BOOL); - php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("fresh_connect"), CURLOPT_FRESH_CONNECT, _IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("forbid_reuse"), CURLOPT_FORBID_REUSE, _IS_BOOL); + + /* outgoing interface */ + php_http_option_register(registry, ZEND_STRL("interface"), CURLOPT_INTERFACE, IS_STRING); + if ((opt = php_http_option_register(registry, ZEND_STRL("portrange"), CURLOPT_LOCALPORT, IS_ARRAY))) { + opt->setter = php_http_curle_option_set_portrange; + } + + /* another endpoint port */ + php_http_option_register(registry, ZEND_STRL("port"), CURLOPT_PORT, IS_LONG); + + /* RFC4007 zone_id */ + #if PHP_HTTP_CURL_VERSION(7,19,0) + php_http_option_register(registry, ZEND_STRL("address_scope"), CURLOPT_ADDRESS_SCOPE, IS_LONG); + #endif + + /* auth */ + if ((opt = php_http_option_register(registry, ZEND_STRL("httpauth"), CURLOPT_USERPWD, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("httpauthtype"), CURLOPT_HTTPAUTH, IS_LONG))) { + Z_LVAL(opt->defval) = CURLAUTH_ANYSAFE; + } + #if PHP_HTTP_CURL_VERSION(7,43,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("service_name"), CURLOPT_SERVICE_NAME, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + #endif + + /* redirects */ + if ((opt = php_http_option_register(registry, ZEND_STRL("redirect"), CURLOPT_FOLLOWLOCATION, IS_LONG))) { + opt->setter = php_http_curle_option_set_redirect; + } - php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("unrestricted_auth"), CURLOPT_UNRESTRICTED_AUTH, _IS_BOOL); + #if PHP_HTTP_CURL_VERSION(7,19,1) + php_http_option_register(registry, ZEND_STRL("postredir"), CURLOPT_POSTREDIR, IS_LONG); + #endif + + /* retries */ + if ((opt = php_http_option_register(registry, ZEND_STRL("retrycount"), 0, IS_LONG))) { + opt->setter = php_http_curle_option_set_retrycount; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("retrydelay"), 0, IS_DOUBLE))) { + opt->setter = php_http_curle_option_set_retrydelay; + } + + /* referer */ + if ((opt = php_http_option_register(registry, ZEND_STRL("referer"), CURLOPT_REFERER, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } - if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("autoreferer"), CURLOPT_AUTOREFERER, _IS_BOOL))) { + ZVAL_BOOL(&opt->defval, 1); + } + + /* useragent */ + if ((opt = php_http_option_register(registry, ZEND_STRL("useragent"), CURLOPT_USERAGENT, IS_STRING))) { + /* don't check strlen, to allow sending no useragent at all */ - ZVAL_STRING(&opt->defval, ++ ZVAL_PSTRING(&opt->defval, + "PECL_HTTP/" PHP_PECL_HTTP_VERSION " " + "PHP/" PHP_VERSION " " - "libcurl/" LIBCURL_VERSION - , 0); ++ "libcurl/" LIBCURL_VERSION); + } + + /* resume */ + if ((opt = php_http_option_register(registry, ZEND_STRL("resume"), CURLOPT_RESUME_FROM, IS_LONG))) { + opt->setter = php_http_curle_option_set_resume; + } + /* ranges */ + if ((opt = php_http_option_register(registry, ZEND_STRL("range"), CURLOPT_RANGE, IS_ARRAY))) { + opt->setter = php_http_curle_option_set_range; + } + + /* etag */ + if ((opt = php_http_option_register(registry, ZEND_STRL("etag"), 0, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->setter = php_http_curle_option_set_etag; + } + + /* compression */ - if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("compress"), 0, _IS_BOOL))) { + opt->setter = php_http_curle_option_set_compress; + } + + /* lastmodified */ + if ((opt = php_http_option_register(registry, ZEND_STRL("lastmodified"), 0, IS_LONG))) { + opt->setter = php_http_curle_option_set_lastmodified; + } + + /* cookies */ - if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("encodecookies"), 0, _IS_BOOL))) { + opt->setter = php_http_curle_option_set_encodecookies; + ZVAL_BOOL(&opt->defval, 1); + } + if ((opt = php_http_option_register(registry, ZEND_STRL("cookies"), 0, IS_ARRAY))) { + opt->setter = php_http_curle_option_set_cookies; + } + + /* cookiesession, don't load session cookies from cookiestore */ - php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("cookiesession"), CURLOPT_COOKIESESSION, _IS_BOOL); + /* cookiestore, read initial cookies from that file and store cookies back into that file */ + if ((opt = php_http_option_register(registry, ZEND_STRL("cookiestore"), 0, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + opt->setter = php_http_curle_option_set_cookiestore; + } + + /* maxfilesize */ + php_http_option_register(registry, ZEND_STRL("maxfilesize"), CURLOPT_MAXFILESIZE, IS_LONG); + + /* http protocol version */ + php_http_option_register(registry, ZEND_STRL("protocol"), CURLOPT_HTTP_VERSION, IS_LONG); + + /* timeouts */ + if ((opt = php_http_option_register(registry, ZEND_STRL("timeout"), CURLOPT_TIMEOUT_MS, IS_DOUBLE))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("connecttimeout"), CURLOPT_CONNECTTIMEOUT_MS, IS_DOUBLE))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS; + Z_DVAL(opt->defval) = 3; + } + #if PHP_HTTP_CURL_VERSION(7,36,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("expect_100_timeout"), CURLOPT_EXPECT_100_TIMEOUT_MS, IS_DOUBLE))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_TRANSFORM_MS; + Z_DVAL(opt->defval) = 1; + } + #endif + + /* tcp */ - php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("tcp_nodelay"), CURLOPT_TCP_NODELAY, _IS_BOOL); + #if PHP_HTTP_CURL_VERSION(7,25,0) - php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("tcp_keepalive"), CURLOPT_TCP_KEEPALIVE, _IS_BOOL); + if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepidle"), CURLOPT_TCP_KEEPIDLE, IS_LONG))) { + Z_LVAL(opt->defval) = 60; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("tcp_keepintvl"), CURLOPT_TCP_KEEPINTVL, IS_LONG))) { + Z_LVAL(opt->defval) = 60; + } + #endif + + /* ssl */ + if ((opt = php_http_option_register(registry, ZEND_STRL("ssl"), 0, IS_ARRAY))) { + registry = &opt->suboptions; + + if ((opt = php_http_option_register(registry, ZEND_STRL("cert"), CURLOPT_SSLCERT, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("certtype"), CURLOPT_SSLCERTTYPE, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; - ZVAL_STRING(&opt->defval, "PEM", 0); ++ ZVAL_PSTRING(&opt->defval, "PEM"); + } + if ((opt = php_http_option_register(registry, ZEND_STRL("key"), CURLOPT_SSLKEY, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("keytype"), CURLOPT_SSLKEYTYPE, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; - ZVAL_STRING(&opt->defval, "PEM", 0); ++ ZVAL_PSTRING(&opt->defval, "PEM"); + } + if ((opt = php_http_option_register(registry, ZEND_STRL("keypasswd"), CURLOPT_SSLKEYPASSWD, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + php_http_option_register(registry, ZEND_STRL("engine"), CURLOPT_SSLENGINE, IS_STRING); + php_http_option_register(registry, ZEND_STRL("version"), CURLOPT_SSLVERSION, IS_LONG); - if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("verifypeer"), CURLOPT_SSL_VERIFYPEER, _IS_BOOL))) { + ZVAL_BOOL(&opt->defval, 1); + } - if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("verifyhost"), CURLOPT_SSL_VERIFYHOST, _IS_BOOL))) { + ZVAL_BOOL(&opt->defval, 1); + opt->setter = php_http_curle_option_set_ssl_verifyhost; + } + #if PHP_HTTP_CURL_VERSION(7,41,0) - php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("verifystatus"), CURLOPT_SSL_VERIFYSTATUS, _IS_BOOL); + #endif + php_http_option_register(registry, ZEND_STRL("cipher_list"), CURLOPT_SSL_CIPHER_LIST, IS_STRING); + if ((opt = php_http_option_register(registry, ZEND_STRL("cainfo"), CURLOPT_CAINFO, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + #ifdef PHP_HTTP_CURL_CAINFO - ZVAL_STRING(&opt->defval, PHP_HTTP_CURL_CAINFO, 0); ++ ZVAL_PSTRING(&opt->defval, PHP_HTTP_CURL_CAINFO); + #endif + } + if ((opt = php_http_option_register(registry, ZEND_STRL("capath"), CURLOPT_CAPATH, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("random_file"), CURLOPT_RANDOM_FILE, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("egdsocket"), CURLOPT_EGDSOCKET, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + #if PHP_HTTP_CURL_VERSION(7,19,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("issuercert"), CURLOPT_ISSUERCERT, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + # ifdef PHP_HTTP_HAVE_OPENSSL + if ((opt = php_http_option_register(registry, ZEND_STRL("crlfile"), CURLOPT_CRLFILE, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + # endif + #endif + #if (PHP_HTTP_CURL_VERSION(7,19,1) && defined(PHP_HTTP_HAVE_OPENSSL)) || (PHP_HTTP_CURL_VERSION(7,34,0) && defined(PHP_HTTP_HAVE_NSS)) || (PHP_HTTP_CURL_VERSION(7,42,0) && defined(PHP_HTTP_HAVE_GNUTLS)) || (PHP_HTTP_CURL_VERSION(7,39,0) && defined(PHP_HTTP_HAVE_GSKIT)) - php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, IS_BOOL); ++ if ((opt = php_http_option_register(registry, ZEND_STRL("certinfo"), CURLOPT_CERTINFO, _IS_BOOL))) { ++ ZVAL_FALSE(&opt->defval); ++ } + #endif + #if PHP_HTTP_CURL_VERSION(7,36,0) - if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("enable_npn"), CURLOPT_SSL_ENABLE_NPN, _IS_BOOL))) { + ZVAL_BOOL(&opt->defval, 1); + } - if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("enable_alpn"), CURLOPT_SSL_ENABLE_ALPN, _IS_BOOL))) { + ZVAL_BOOL(&opt->defval, 1); + } + #endif + #if PHP_HTTP_CURL_VERSION(7,39,0) + /* FIXME: see http://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html#AVAILABILITY */ + if ((opt = php_http_option_register(registry, ZEND_STRL("pinned_publickey"), CURLOPT_PINNEDPUBLICKEY, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR; + } + #endif + #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthtype"), CURLOPT_TLSAUTH_TYPE, IS_LONG))) { + opt->setter = php_http_curle_option_set_ssl_tlsauthtype; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthuser"), CURLOPT_TLSAUTH_USERNAME, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + if ((opt = php_http_option_register(registry, ZEND_STRL("tlsauthpass"), CURLOPT_TLSAUTH_PASSWORD, IS_STRING))) { + opt->flags |= PHP_HTTP_CURLE_OPTION_CHECK_STRLEN; + } + #endif + #if PHP_HTTP_CURL_VERSION(7,42,0) && (defined(PHP_HTTP_HAVE_NSS) || defined(PHP_HTTP_HAVE_DARWINSSL)) - php_http_option_register(registry, ZEND_STRL("falsestart"), CURLOPT_SSL_FALSESTART, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("falsestart"), CURLOPT_SSL_FALSESTART, _IS_BOOL); + #endif + } + } + + static zval *php_http_curle_get_option(php_http_option_t *opt, HashTable *options, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + zval *option; + + if ((option = php_http_option_get(opt, options, NULL))) { - option = php_http_ztyp(opt->type, option); - zend_hash_quick_update(&curl->options.cache, opt->name.s, opt->name.l, opt->name.h, &option, sizeof(zval *), NULL); ++ zval zopt; ++ ++ ZVAL_DUP(&zopt, option); ++ convert_to_explicit_type(&zopt, opt->type); ++ zend_hash_update(&curl->options.cache, opt->name, &zopt); ++ return zend_hash_find(&curl->options.cache, opt->name); + } + return option; + } + + static ZEND_RESULT_CODE php_http_curle_set_option(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_curl_handler_t *curl = userdata; + CURL *ch = curl->handle; + zval tmp; + CURLcode rc = CURLE_OK; + ZEND_RESULT_CODE rv = SUCCESS; - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + if (!val) { + val = &opt->defval; + } + + switch (opt->type) { - case IS_BOOL: ++ case _IS_BOOL: + if (opt->setter) { + rv = opt->setter(opt, val, curl); - } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) { ++ } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) (Z_TYPE_P(val) == IS_TRUE))) { + rv = FAILURE; + } + break; + + case IS_LONG: + if (opt->setter) { + rv = opt->setter(opt, val, curl); + } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, Z_LVAL_P(val))) { + rv = FAILURE; + } + break; + + case IS_STRING: + if (opt->setter) { + rv = opt->setter(opt, val, curl); ++ } else if (!val || Z_TYPE_P(val) == IS_NULL) { ++ if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) { ++ rv = FAILURE; ++ } + } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_STRLEN) && !Z_STRLEN_P(val)) { + if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) { + rv = FAILURE; + } - } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR) && Z_STRVAL_P(val) && SUCCESS != php_check_open_basedir(Z_STRVAL_P(val) TSRMLS_CC)) { ++ } else if ((opt->flags & PHP_HTTP_CURLE_OPTION_CHECK_BASEDIR) && Z_STRVAL_P(val) && SUCCESS != php_check_open_basedir(Z_STRVAL_P(val))) { + if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, NULL))) { + rv = FAILURE; + } + } else if (CURLE_OK != (rc = curl_easy_setopt(ch, opt->option, Z_STRVAL_P(val)))) { + rv = FAILURE; + } + break; + + case IS_DOUBLE: + if (opt->flags & PHP_HTTP_CURLE_OPTION_TRANSFORM_MS) { + tmp = *val; + Z_DVAL(tmp) *= 1000; + val = &tmp; + } + if (opt->setter) { + rv = opt->setter(opt, val, curl); + } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_DVAL_P(val))) { + rv = FAILURE; + } + break; + + case IS_ARRAY: + if (opt->setter) { + rv = opt->setter(opt, val, curl); + } else if (Z_TYPE_P(val) != IS_NULL) { + rv = php_http_options_apply(&opt->suboptions, Z_ARRVAL_P(val), curl); + } + break; + + default: + if (opt->setter) { + rv = opt->setter(opt, val, curl); + } else { + rv = FAILURE; + } + break; + } + if (rv != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc)); ++ php_error_docref(NULL, E_NOTICE, "Could not set option %s (%s)", opt->name->val, curl_easy_strerror(rc)); + } + return rv; + } + + #if PHP_HTTP_CURL_VERSION(7,30,0) + static ZEND_RESULT_CODE php_http_curlm_option_set_pipelining_bl(php_http_option_t *opt, zval *value, void *userdata) + { + php_http_client_t *client = userdata; + php_http_client_curl_t *curl = client->ctx; + CURLM *ch = curl->handle; + HashTable tmp_ht; + char **bl = NULL; - TSRMLS_FETCH_FROM_CTX(client->ts); + + /* array of char *, ending with a NULL */ + if (value && Z_TYPE_P(value) != IS_NULL) { - zval **entry; - HashPosition pos; ++ zval *entry; + HashTable *ht = HASH_OF(value); + int c = zend_hash_num_elements(ht); + char **ptr = ecalloc(c + 1, sizeof(char *)); + + bl = ptr; + + zend_hash_init(&tmp_ht, c, NULL, ZVAL_PTR_DTOR, 0); + array_join(ht, &tmp_ht, 0, ARRAY_JOIN_STRINGIFY); + - FOREACH_HASH_VAL(pos, &tmp_ht, entry) { - *ptr++ = Z_STRVAL_PP(entry); ++ ZEND_HASH_FOREACH_VAL(&tmp_ht, entry) ++ { ++ *ptr++ = Z_STRVAL_P(entry); + } ++ ZEND_HASH_FOREACH_END(); + } + + if (CURLM_OK != curl_multi_setopt(ch, opt->option, bl)) { + if (bl) { + efree(bl); + zend_hash_destroy(&tmp_ht); + } + return FAILURE; + } + + if (bl) { + efree(bl); + zend_hash_destroy(&tmp_ht); + } + return SUCCESS; + } + #endif + + #if PHP_HTTP_HAVE_EVENT + static inline ZEND_RESULT_CODE php_http_curlm_use_eventloop(php_http_client_t *h, zend_bool enable) + { + php_http_client_curl_t *curl = h->ctx; + + if ((curl->useevents = enable)) { + if (!curl->evbase) { + curl->evbase = event_base_new(); + } + if (!curl->timeout) { + curl->timeout = ecalloc(1, sizeof(struct event)); + } + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, h); + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, php_http_curlm_socket_callback); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, h); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, php_http_curlm_timer_callback); + } else { + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETDATA, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_SOCKETFUNCTION, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERDATA, NULL); + curl_multi_setopt(curl->handle, CURLMOPT_TIMERFUNCTION, NULL); + } + + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_curlm_option_set_use_eventloop(php_http_option_t *opt, zval *value, void *userdata) + { + php_http_client_t *client = userdata; + - return php_http_curlm_use_eventloop(client, value && Z_BVAL_P(value)); ++ return php_http_curlm_use_eventloop(client, value && Z_TYPE_P(value) == IS_TRUE); + } + #endif + -static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC) ++static void php_http_curlm_options_init(php_http_options_t *registry) + { + php_http_option_t *opt; + + /* set size of connection cache */ + if ((opt = php_http_option_register(registry, ZEND_STRL("maxconnects"), CURLMOPT_MAXCONNECTS, IS_LONG))) { + /* -1 == default, 0 == unlimited */ + ZVAL_LONG(&opt->defval, -1); + } + /* set max number of connections to a single host */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("max_host_connections"), CURLMOPT_MAX_HOST_CONNECTIONS, IS_LONG); + #endif + /* maximum number of requests in a pipeline */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("max_pipeline_length"), CURLMOPT_MAX_PIPELINE_LENGTH, IS_LONG))) { + ZVAL_LONG(&opt->defval, 5); + } + #endif + /* max simultaneously open connections */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("max_total_connections"), CURLMOPT_MAX_TOTAL_CONNECTIONS, IS_LONG); + #endif + /* enable/disable HTTP pipelining */ - php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, IS_BOOL); ++ php_http_option_register(registry, ZEND_STRL("pipelining"), CURLMOPT_PIPELINING, _IS_BOOL); + /* chunk length threshold for pipelining */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("chunk_length_penalty_size"), CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, IS_LONG); + #endif + /* size threshold for pipelining penalty */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + php_http_option_register(registry, ZEND_STRL("content_length_penalty_size"), CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, IS_LONG); + #endif + /* pipelining server blacklist */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_server_bl"), CURLMOPT_PIPELINING_SERVER_BL, IS_ARRAY))) { + opt->setter = php_http_curlm_option_set_pipelining_bl; + } + #endif + /* pipelining host blacklist */ + #if PHP_HTTP_CURL_VERSION(7,30,0) + if ((opt = php_http_option_register(registry, ZEND_STRL("pipelining_site_bl"), CURLMOPT_PIPELINING_SITE_BL, IS_ARRAY))) { + opt->setter = php_http_curlm_option_set_pipelining_bl; + } + #endif + /* events */ + #if PHP_HTTP_HAVE_EVENT - if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, IS_BOOL))) { ++ if ((opt = php_http_option_register(registry, ZEND_STRL("use_eventloop"), 0, _IS_BOOL))) { + opt->setter = php_http_curlm_option_set_use_eventloop; + } + #endif + } + + static ZEND_RESULT_CODE php_http_curlm_set_option(php_http_option_t *opt, zval *val, void *userdata) + { + php_http_client_t *client = userdata; + php_http_client_curl_t *curl = client->ctx; + CURLM *ch = curl->handle; + zval *orig = val; + CURLMcode rc = CURLM_UNKNOWN_OPTION; + ZEND_RESULT_CODE rv = SUCCESS; - TSRMLS_FETCH_FROM_CTX(client->ts); + + if (!val) { + val = &opt->defval; + } else if (opt->type && Z_TYPE_P(val) != opt->type && !(Z_TYPE_P(val) == IS_NULL && opt->type == IS_ARRAY)) { - val = php_http_ztyp(opt->type, val); ++ zval zopt; ++ ++ ZVAL_DUP(&zopt, val); ++ convert_to_explicit_type(&zopt, opt->type); ++ ++ val = &zopt; + } + + if (opt->setter) { + rv = opt->setter(opt, val, client); + } else { + switch (opt->type) { - case IS_BOOL: - if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) Z_BVAL_P(val)))) { ++ case _IS_BOOL: ++ if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, (long) zend_is_true(val)))) { + rv = FAILURE; + } + break; + case IS_LONG: + if (CURLM_OK != (rc = curl_multi_setopt(ch, opt->option, Z_LVAL_P(val)))) { + rv = FAILURE; + } + break; + default: + rv = FAILURE; + break; + } + } + + if (val && val != orig && val != &opt->defval) { - zval_ptr_dtor(&val); ++ zval_ptr_dtor(val); + } + + if (rv != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, curl_easy_strerror(rc)); ++ php_error_docref(NULL, E_NOTICE, "Could not set option %s (%s)", opt->name->val, curl_easy_strerror(rc)); + } + return rv; + } + + /* client ops */ + + static ZEND_RESULT_CODE php_http_client_curl_handler_reset(php_http_client_curl_handler_t *curl) + { + CURL *ch = curl->handle; + php_http_curle_storage_t *st; + + if ((st = php_http_curle_get_storage(ch))) { + if (st->url) { + pefree(st->url, 1); + st->url = NULL; + } + if (st->cookiestore) { + pefree(st->cookiestore, 1); + st->cookiestore = NULL; + } + st->errorbuffer[0] = '\0'; + } + + curl_easy_setopt(ch, CURLOPT_URL, NULL); + curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(ch, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(ch, CURLOPT_NOBODY, 0L); + /* libcurl < 7.19.6 does not clear auth info with USERPWD set to NULL */ + #if PHP_HTTP_CURL_VERSION(7,19,1) + curl_easy_setopt(ch, CURLOPT_PROXYUSERNAME, NULL); + curl_easy_setopt(ch, CURLOPT_PROXYPASSWORD, NULL); + curl_easy_setopt(ch, CURLOPT_USERNAME, NULL); + curl_easy_setopt(ch, CURLOPT_PASSWORD, NULL); + #endif + + #if PHP_HTTP_CURL_VERSION(7,21,3) + if (curl->options.resolve) { + curl_slist_free_all(curl->options.resolve); + curl->options.resolve = NULL; + } + #endif + curl->options.retry.count = 0; + curl->options.retry.delay = 0; + curl->options.redirects = 0; + curl->options.encode_cookies = 1; + + if (curl->options.headers) { + curl_slist_free_all(curl->options.headers); + curl->options.headers = NULL; + } + if (curl->options.proxyheaders) { + curl_slist_free_all(curl->options.proxyheaders); + curl->options.proxyheaders = NULL; + } + + php_http_buffer_reset(&curl->options.cookies); + php_http_buffer_reset(&curl->options.ranges); + + return SUCCESS; + } + + static php_http_client_curl_handler_t *php_http_client_curl_handler_init(php_http_client_t *h, php_resource_factory_t *rf) + { + void *handle; + php_http_client_curl_handler_t *handler; - TSRMLS_FETCH_FROM_CTX(h->ts); + - if (!(handle = php_resource_factory_handle_ctor(rf, NULL TSRMLS_CC))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle"); ++ if (!(handle = php_resource_factory_handle_ctor(rf, NULL))) { ++ php_error_docref(NULL, E_WARNING, "Failed to initialize curl handle"); + return NULL; + } + + handler = ecalloc(1, sizeof(*handler)); + handler->rf = rf; + handler->client = h; + handler->handle = handle; - handler->response.body = php_http_message_body_init(NULL, NULL TSRMLS_CC); ++ handler->response.body = php_http_message_body_init(NULL, NULL); + php_http_buffer_init(&handler->response.headers); + php_http_buffer_init(&handler->options.cookies); + php_http_buffer_init(&handler->options.ranges); + zend_hash_init(&handler->options.cache, 0, NULL, ZVAL_PTR_DTOR, 0); + + #if defined(ZTS) + curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L); + #endif + curl_easy_setopt(handle, CURLOPT_HEADER, 0L); + curl_easy_setopt(handle, CURLOPT_FILETIME, 1L); + curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L); + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, php_http_curle_header_callback); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, php_http_curle_body_callback); + curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, php_http_curle_raw_callback); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, php_http_curle_read_callback); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, php_http_curle_seek_callback); + #if PHP_HTTP_CURL_VERSION(7,32,0) + curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, php_http_curle_xferinfo_callback); + curl_easy_setopt(handle, CURLOPT_XFERINFODATA, handler); + #else + curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, php_http_curle_progress_callback); + curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, handler); + #endif + curl_easy_setopt(handle, CURLOPT_DEBUGDATA, handler); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, handler); + curl_easy_setopt(handle, CURLOPT_HEADERDATA, handler); + + php_http_client_curl_handler_reset(handler); + + return handler; + } + + + static ZEND_RESULT_CODE php_http_client_curl_handler_prepare(php_http_client_curl_handler_t *curl, php_http_client_enqueue_t *enqueue) + { + size_t body_size; + php_http_message_t *msg = enqueue->request; + php_http_curle_storage_t *storage = php_http_curle_get_storage(curl->handle); - TSRMLS_FETCH_FROM_CTX(curl->client->ts); + + /* request url */ + if (!PHP_HTTP_INFO(msg).request.url) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL"); ++ php_error_docref(NULL, E_WARNING, "Cannot request empty URL"); + return FAILURE; + } + storage->errorbuffer[0] = '\0'; + if (storage->url) { + pefree(storage->url, 1); + } + php_http_url_to_string(PHP_HTTP_INFO(msg).request.url, &storage->url, NULL, 1); + curl_easy_setopt(curl->handle, CURLOPT_URL, storage->url); + + /* apply options */ + php_http_options_apply(&php_http_curle_options, enqueue->options, curl); + + /* request headers */ + php_http_message_update_headers(msg); + if (zend_hash_num_elements(&msg->hdrs)) { - php_http_array_hashkey_t header_key = php_http_array_hashkey_init(0); - zval **header_val, *header_cpy; - HashPosition pos; ++ php_http_arrkey_t header_key; ++ zval *header_val; ++ zend_string *header_str; + php_http_buffer_t header; + #if !PHP_HTTP_CURL_VERSION(7,23,0) - zval **ct = NULL; - - zend_hash_find(&msg->hdrs, ZEND_STRS("Content-Length"), (void *) &ct); ++ zval *ct = zend_hash_str_find(&msg->hdrs, ZEND_STRL("Content-Length")); + #endif + + php_http_buffer_init(&header); - FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) { - if (header_key.type == HASH_KEY_IS_STRING) { ++ ZEND_HASH_FOREACH_KEY_VAL(&msg->hdrs, header_key.h, header_key.key, header_val) ++ { ++ if (header_key.key) { + #if !PHP_HTTP_CURL_VERSION(7,23,0) + /* avoid duplicate content-length header */ - if (ct && *ct == *header_val) { ++ if (ct && ct == header_val) { + continue; + } + #endif - header_cpy = php_http_ztyp(IS_STRING, *header_val); - php_http_buffer_appendf(&header, "%s: %s", header_key.str, Z_STRVAL_P(header_cpy)); ++ header_str = zval_get_string(header_val); ++ php_http_buffer_appendf(&header, "%s: %s", header_key.key->val, header_str->val); + php_http_buffer_fix(&header); + curl->options.headers = curl_slist_append(curl->options.headers, header.data); + php_http_buffer_reset(&header); - - zval_ptr_dtor(&header_cpy); ++ zend_string_release(header_str); + } + } ++ ZEND_HASH_FOREACH_END(); + php_http_buffer_dtor(&header); + } + curl_easy_setopt(curl->handle, CURLOPT_HTTPHEADER, curl->options.headers); + + /* attach request body */ + if ((body_size = php_http_message_body_size(msg->body))) { + /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the + * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.« + * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the + * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method + * does not allow a request body. + */ + php_stream_rewind(php_http_message_body_stream(msg->body)); + curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, msg->body); + curl_easy_setopt(curl->handle, CURLOPT_READDATA, msg->body); + curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, body_size); + curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, body_size); + curl_easy_setopt(curl->handle, CURLOPT_POST, 1L); + } else { + curl_easy_setopt(curl->handle, CURLOPT_SEEKDATA, NULL); + curl_easy_setopt(curl->handle, CURLOPT_READDATA, NULL); + curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE, 0L); + curl_easy_setopt(curl->handle, CURLOPT_POSTFIELDSIZE, 0L); + } + + /* + * Always use CUSTOMREQUEST, else curl won't send any request body for GET etc. + * See e.g. bug #69313. + * + * Here's what curl does: + * - CURLOPT_HTTPGET: ignore request body + * - CURLOPT_UPLOAD: set "Expect: 100-continue" header + * - CURLOPT_POST: set "Content-Type: application/x-www-form-urlencoded" header + * Now select the least bad. + * + * See also https://tools.ietf.org/html/rfc7231#section-5.1.1 + */ + if (PHP_HTTP_INFO(msg).request.method) { + switch(php_http_select_str(PHP_HTTP_INFO(msg).request.method, 2, "HEAD", "PUT")) { + case 0: + curl_easy_setopt(curl->handle, CURLOPT_NOBODY, 1L); + break; + case 1: + curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L); + break; + default: + curl_easy_setopt(curl->handle, CURLOPT_CUSTOMREQUEST, PHP_HTTP_INFO(msg).request.method); + } + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot use empty request method"); ++ php_error_docref(NULL, E_WARNING, "Cannot use empty request method"); + return FAILURE; + } + + return SUCCESS; + } + + static void php_http_client_curl_handler_clear(php_http_client_curl_handler_t *handler) + { + curl_easy_setopt(handler->handle, CURLOPT_NOPROGRESS, 1L); + #if PHP_HTTP_CURL_VERSION(7,32,0) + curl_easy_setopt(handler->handle, CURLOPT_XFERINFOFUNCTION, NULL); + #else + curl_easy_setopt(handler->handle, CURLOPT_PROGRESSFUNCTION, NULL); + #endif + curl_easy_setopt(handler->handle, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(handler->handle, CURLOPT_DEBUGFUNCTION, NULL); + } + + static void php_http_client_curl_handler_dtor(php_http_client_curl_handler_t *handler) + { - TSRMLS_FETCH_FROM_CTX(handler->client->ts); - + php_http_client_curl_handler_clear(handler); + - php_resource_factory_handle_dtor(handler->rf, handler->handle TSRMLS_CC); ++ php_resource_factory_handle_dtor(handler->rf, handler->handle); + php_resource_factory_free(&handler->rf); + + php_http_message_body_free(&handler->response.body); + php_http_buffer_dtor(&handler->response.headers); + php_http_buffer_dtor(&handler->options.ranges); + php_http_buffer_dtor(&handler->options.cookies); + zend_hash_destroy(&handler->options.cache); + ++#if PHP_HTTP_CURL_VERSION(7,21,3) ++ if (handler->options.resolve) { ++ curl_slist_free_all(handler->options.resolve); ++ handler->options.resolve = NULL; ++ } ++#endif ++ + if (handler->options.headers) { + curl_slist_free_all(handler->options.headers); + handler->options.headers = NULL; + } + ++ if (handler->options.proxyheaders) { ++ curl_slist_free_all(handler->options.proxyheaders); ++ handler->options.proxyheaders = NULL; ++ } ++ + efree(handler); + } + + static php_http_client_t *php_http_client_curl_init(php_http_client_t *h, void *handle) + { + php_http_client_curl_t *curl; - TSRMLS_FETCH_FROM_CTX(h->ts); + - if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL TSRMLS_CC))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize curl handle"); ++ if (!handle && !(handle = php_resource_factory_handle_ctor(h->rf, NULL))) { ++ php_error_docref(NULL, E_WARNING, "Failed to initialize curl handle"); + return NULL; + } + + curl = ecalloc(1, sizeof(*curl)); + curl->handle = handle; + curl->unfinished = 0; + h->ctx = curl; + + return h; + } + + static void php_http_client_curl_dtor(php_http_client_t *h) + { + php_http_client_curl_t *curl = h->ctx; - TSRMLS_FETCH_FROM_CTX(h->ts); + + #if PHP_HTTP_HAVE_EVENT + if (curl->timeout) { + if (event_initialized(curl->timeout) && event_pending(curl->timeout, EV_TIMEOUT, NULL)) { + event_del(curl->timeout); + } + efree(curl->timeout); + curl->timeout = NULL; + } + if (curl->evbase) { + event_base_free(curl->evbase); + curl->evbase = NULL; + } + #endif + curl->unfinished = 0; + - php_resource_factory_handle_dtor(h->rf, curl->handle TSRMLS_CC); ++ php_resource_factory_handle_dtor(h->rf, curl->handle); + + efree(curl); + h->ctx = NULL; + } + + static void queue_dtor(php_http_client_enqueue_t *e) + { + php_http_client_curl_handler_t *handler = e->opaque; + + if (handler->queue.dtor) { + e->opaque = handler->queue.opaque; + handler->queue.dtor(e); + } + php_http_client_curl_handler_dtor(handler); + } + -static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue TSRMLS_DC) ++static php_resource_factory_t *create_rf(php_http_client_t *h, php_http_client_enqueue_t *enqueue) + { + php_persistent_handle_factory_t *pf = NULL; + php_resource_factory_t *rf = NULL; + php_http_url_t *url = enqueue->request->http.info.request.url; + + if (!url || (!url->host && !url->path)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot request empty URL"); ++ php_error_docref(NULL, E_WARNING, "Cannot request empty URL"); + return NULL; + } + + /* only if the client itself is setup for persistence */ + if (php_resource_factory_is_persistent(h->rf)) { ++ zend_string *id; + char *id_str = NULL; + size_t id_len; + int port = url->port ? url->port : 80; - zval **zport; ++ zval *zport; + - if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) { - zval *zcpy = php_http_ztyp(IS_LONG, *zport); ++ if ((zport = zend_hash_str_find(enqueue->options, ZEND_STRL("port")))) { ++ zend_long lport = zval_get_long(zport); + - if (Z_LVAL_P(zcpy)) { - port = Z_LVAL_P(zcpy); ++ if (lport > 0) { ++ port = lport; + } - zval_ptr_dtor(&zcpy); + } + + id_len = spprintf(&id_str, 0, "%s:%d", STR_PTR(url->host), port); - pf = php_persistent_handle_concede(NULL, ZEND_STRL("http\\Client\\Curl\\Request"), id_str, id_len, NULL, NULL TSRMLS_CC); - efree(id_str); ++ id = php_http_cs2zs(id_str, id_len); ++ pf = php_persistent_handle_concede(NULL, PHP_HTTP_G->client.curl.driver.request_name, id, NULL, NULL); ++ zend_string_release(id); + } + + if (pf) { + rf = php_persistent_handle_resource_factory_init(NULL, pf); + } else { + rf = php_resource_factory_init(NULL, &php_http_curle_resource_factory_ops, NULL, NULL); + } + + return rf; + } + + static ZEND_RESULT_CODE php_http_client_curl_enqueue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) + { + CURLMcode rs; + php_http_client_curl_t *curl = h->ctx; + php_http_client_curl_handler_t *handler; + php_http_client_progress_state_t *progress; + php_resource_factory_t *rf; - TSRMLS_FETCH_FROM_CTX(h->ts); + - rf = create_rf(h, enqueue TSRMLS_CC); ++ rf = create_rf(h, enqueue); + if (!rf) { + return FAILURE; + } + + handler = php_http_client_curl_handler_init(h, rf); + if (!handler) { + return FAILURE; + } + + if (SUCCESS != php_http_client_curl_handler_prepare(handler, enqueue)) { + php_http_client_curl_handler_dtor(handler); + return FAILURE; + } + + handler->queue = *enqueue; + enqueue->opaque = handler; + enqueue->dtor = queue_dtor; + + if (CURLM_OK == (rs = curl_multi_add_handle(curl->handle, handler->handle))) { + zend_llist_add_element(&h->requests, enqueue); + ++curl->unfinished; + + if (h->callback.progress.func && SUCCESS == php_http_client_getopt(h, PHP_HTTP_CLIENT_OPT_PROGRESS_INFO, enqueue->request, &progress)) { + progress->info = "start"; + h->callback.progress.func(h->callback.progress.arg, h, &handler->queue, progress); + progress->started = 1; + } + + return SUCCESS; + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs)); ++ php_error_docref(NULL, E_WARNING, "Could not enqueue request: %s", curl_multi_strerror(rs)); + return FAILURE; + } + } + + static ZEND_RESULT_CODE php_http_client_curl_dequeue(php_http_client_t *h, php_http_client_enqueue_t *enqueue) + { + CURLMcode rs; + php_http_client_curl_t *curl = h->ctx; + php_http_client_curl_handler_t *handler = enqueue->opaque; - TSRMLS_FETCH_FROM_CTX(h->ts); + + php_http_client_curl_handler_clear(handler); + if (CURLM_OK == (rs = curl_multi_remove_handle(curl->handle, handler->handle))) { + zend_llist_del_element(&h->requests, handler->handle, (int (*)(void *, void *)) compare_queue); + return SUCCESS; + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs)); ++ php_error_docref(NULL, E_WARNING, "Could not dequeue request: %s", curl_multi_strerror(rs)); + } + + return FAILURE; + } + + static void php_http_client_curl_reset(php_http_client_t *h) + { + zend_llist_element *next_el, *this_el; + + for (this_el = h->requests.head; this_el; this_el = next_el) { + next_el = this_el->next; + php_http_client_curl_dequeue(h, (void *) this_el->data); + } + } + + static inline void php_http_client_curl_get_timeout(php_http_client_curl_t *curl, long max_tout, struct timeval *timeout) + { + if ((CURLM_OK == curl_multi_timeout(curl->handle, &max_tout)) && (max_tout > 0)) { + timeout->tv_sec = max_tout / 1000; + timeout->tv_usec = (max_tout % 1000) * 1000; + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 1000; + } + } + + #ifdef PHP_WIN32 + # define SELECT_ERROR SOCKET_ERROR + #else + # define SELECT_ERROR -1 + #endif + + static ZEND_RESULT_CODE php_http_client_curl_wait(php_http_client_t *h, struct timeval *custom_timeout) + { + int MAX; + fd_set R, W, E; + struct timeval timeout; + php_http_client_curl_t *curl = h->ctx; + + #if PHP_HTTP_HAVE_EVENT + if (curl->useevents) { + if (!event_initialized(curl->timeout)) { + event_assign(curl->timeout, curl->evbase, CURL_SOCKET_TIMEOUT, 0, php_http_curlm_timeout_callback, h); + } else if (custom_timeout && timerisset(custom_timeout)) { + event_add(curl->timeout, custom_timeout); + } else if (!event_pending(curl->timeout, EV_TIMEOUT, NULL)) { + php_http_client_curl_get_timeout(curl, 1000, &timeout); + event_add(curl->timeout, &timeout); + } + + event_base_loop(curl->evbase, EVLOOP_ONCE); + + return SUCCESS; + } + #endif + + FD_ZERO(&R); + FD_ZERO(&W); + FD_ZERO(&E); + + if (CURLM_OK == curl_multi_fdset(curl->handle, &R, &W, &E, &MAX)) { + if (custom_timeout && timerisset(custom_timeout)) { + timeout = *custom_timeout; + } else { + php_http_client_curl_get_timeout(curl, 1000, &timeout); + } + + if (MAX == -1) { + php_http_sleep((double) timeout.tv_sec + (double) (timeout.tv_usec / PHP_HTTP_MCROSEC)); + return SUCCESS; + } else if (SELECT_ERROR != select(MAX + 1, &R, &W, &E, &timeout)) { + return SUCCESS; + } + } + return FAILURE; + } + + static int php_http_client_curl_once(php_http_client_t *h) + { + php_http_client_curl_t *curl = h->ctx; + + #if PHP_HTTP_HAVE_EVENT + if (curl->useevents) { + event_base_loop(curl->evbase, EVLOOP_NONBLOCK); + } else + #endif + while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(curl->handle, &curl->unfinished)); + + php_http_curlm_responsehandler(h); + + return curl->unfinished; + + } + + static ZEND_RESULT_CODE php_http_client_curl_exec(php_http_client_t *h) + { + #if PHP_HTTP_HAVE_EVENT + php_http_client_curl_t *curl = h->ctx; -#endif - TSRMLS_FETCH_FROM_CTX(h->ts); + -#if PHP_HTTP_HAVE_EVENT + if (curl->useevents) { + php_http_curlm_timeout_callback(CURL_SOCKET_TIMEOUT, /*EV_READ|EV_WRITE*/0, h); + do { + int ev_rc = event_base_dispatch(curl->evbase); + + #if DBG_EVENTS + fprintf(stderr, "%c", "X.0"[ev_rc+1]); + #endif + + if (ev_rc < 0) { - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error in event_base_dispatch()"); ++ php_error_docref(NULL, E_ERROR, "Error in event_base_dispatch()"); + return FAILURE; + } + } while (curl->unfinished && !EG(exception)); + } else + #endif + { + while (php_http_client_curl_once(h) && !EG(exception)) { + if (SUCCESS != php_http_client_curl_wait(h, NULL)) { + #ifdef PHP_WIN32 + /* see http://msdn.microsoft.com/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "WinSock error: %d", WSAGetLastError()); ++ php_error_docref(NULL, E_WARNING, "WinSock error: %d", WSAGetLastError()); + #else - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno)); ++ php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + #endif + return FAILURE; + } + } + } + + return SUCCESS; + } + + static ZEND_RESULT_CODE php_http_client_curl_setopt(php_http_client_t *h, php_http_client_setopt_opt_t opt, void *arg) + { + php_http_client_curl_t *curl = h->ctx; + + switch (opt) { + case PHP_HTTP_CLIENT_OPT_CONFIGURATION: + return php_http_options_apply(&php_http_curlm_options, (HashTable *) arg, h); + break; + + case PHP_HTTP_CLIENT_OPT_ENABLE_PIPELINING: + if (CURLM_OK != curl_multi_setopt(curl->handle, CURLMOPT_PIPELINING, (long) *((zend_bool *) arg))) { + return FAILURE; + } + break; + + case PHP_HTTP_CLIENT_OPT_USE_EVENTS: + #if PHP_HTTP_HAVE_EVENT + return php_http_curlm_use_eventloop(h, *(zend_bool *) arg); + break; + #endif + + default: + return FAILURE; + } + return SUCCESS; + } + -static int apply_available_options(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) ++static int apply_available_options(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key) + { - php_http_option_t *opt = pDest; ++ php_http_option_t *opt = Z_PTR_P(pDest); + HashTable *ht; - zval *entry; ++ zval entry; + int c; + + ht = va_arg(args, HashTable*); + - MAKE_STD_ZVAL(entry); - + if ((c = zend_hash_num_elements(&opt->suboptions.options))) { - array_init_size(entry, c); - zend_hash_apply_with_arguments(&opt->suboptions.options TSRMLS_CC, apply_available_options, 1, Z_ARRVAL_P(entry)); ++ array_init_size(&entry, c); ++ zend_hash_apply_with_arguments(&opt->suboptions.options, apply_available_options, 1, Z_ARRVAL(entry)); + } else { + /* catch deliberate NULL options */ + if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) { - ZVAL_NULL(entry); ++ ZVAL_NULL(&entry); + } else { - ZVAL_COPY_VALUE(entry, &opt->defval); - zval_copy_ctor(entry); ++ ZVAL_ZVAL(&entry, &opt->defval, 1, 0); + } + } + - if (hash_key->nKeyLength) { - zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL); ++ if (hash_key->key) { ++ zend_hash_update(ht, hash_key->key, &entry); + } else { - zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL); ++ zend_hash_index_update(ht, hash_key->h, &entry); + } + + return ZEND_HASH_APPLY_KEEP; + } + + static ZEND_RESULT_CODE php_http_client_curl_getopt(php_http_client_t *h, php_http_client_getopt_opt_t opt, void *arg, void **res) + { + php_http_client_enqueue_t *enqueue; - TSRMLS_FETCH_FROM_CTX(h->ts); + + switch (opt) { + case PHP_HTTP_CLIENT_OPT_PROGRESS_INFO: + if ((enqueue = php_http_client_enqueued(h, arg, NULL))) { + php_http_client_curl_handler_t *handler = enqueue->opaque; + + *((php_http_client_progress_state_t **) res) = &handler->progress; + return SUCCESS; + } + break; + + case PHP_HTTP_CLIENT_OPT_TRANSFER_INFO: + if ((enqueue = php_http_client_enqueued(h, arg, NULL))) { + php_http_client_curl_handler_t *handler = enqueue->opaque; + + php_http_curle_get_info(handler->handle, *(HashTable **) res); + return SUCCESS; + } + break; + + case PHP_HTTP_CLIENT_OPT_AVAILABLE_OPTIONS: - zend_hash_apply_with_arguments(&php_http_curle_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res); ++ zend_hash_apply_with_arguments(&php_http_curle_options.options, apply_available_options, 1, *(HashTable **) res); + break; + + case PHP_HTTP_CLIENT_OPT_AVAILABLE_CONFIGURATION: - zend_hash_apply_with_arguments(&php_http_curlm_options.options TSRMLS_CC, apply_available_options, 1, *(HashTable **) res); ++ zend_hash_apply_with_arguments(&php_http_curlm_options.options, apply_available_options, 1, *(HashTable **) res); + break; + + default: + break; + } + + return FAILURE; + } + + static php_http_client_ops_t php_http_client_curl_ops = { + &php_http_curlm_resource_factory_ops, + php_http_client_curl_init, + NULL /* copy */, + php_http_client_curl_dtor, + php_http_client_curl_reset, + php_http_client_curl_exec, + php_http_client_curl_wait, + php_http_client_curl_once, + php_http_client_curl_enqueue, + php_http_client_curl_dequeue, + php_http_client_curl_setopt, + php_http_client_curl_getopt + }; + + php_http_client_ops_t *php_http_client_curl_get_ops(void) + { + return &php_http_client_curl_ops; + } + + PHP_MINIT_FUNCTION(http_client_curl) + { + php_http_options_t *options; - php_http_client_driver_t driver = { - ZEND_STRL("curl"), - &php_http_client_curl_ops - }; + - if (SUCCESS != php_http_client_driver_add(&driver)) { ++ PHP_HTTP_G->client.curl.driver.driver_name = zend_string_init(ZEND_STRL("curl"), 1); ++ PHP_HTTP_G->client.curl.driver.client_name = zend_string_init(ZEND_STRL("http\\Client\\Curl"), 1); ++ PHP_HTTP_G->client.curl.driver.request_name = zend_string_init(ZEND_STRL("http\\Client\\Curl\\Request"), 1); ++ PHP_HTTP_G->client.curl.driver.client_ops = &php_http_client_curl_ops; ++ ++ if (SUCCESS != php_http_client_driver_add(&PHP_HTTP_G->client.curl.driver)) { + return FAILURE; + } + - if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) { ++ if (SUCCESS != php_persistent_handle_provide(PHP_HTTP_G->client.curl.driver.client_name, &php_http_curlm_resource_factory_ops, NULL, NULL)) { + return FAILURE; + } - if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) { ++ if (SUCCESS != php_persistent_handle_provide(PHP_HTTP_G->client.curl.driver.request_name, &php_http_curle_resource_factory_ops, NULL, NULL)) { + return FAILURE; + } + + if ((options = php_http_options_init(&php_http_curle_options, 1))) { + options->getter = php_http_curle_get_option; + options->setter = php_http_curle_set_option; + - php_http_curle_options_init(options TSRMLS_CC); ++ php_http_curle_options_init(options); + } + if ((options = php_http_options_init(&php_http_curlm_options, 1))) { + options->getter = php_http_option_get; + options->setter = php_http_curlm_set_option; + - php_http_curlm_options_init(options TSRMLS_CC); ++ php_http_curlm_options_init(options); + } + + /* + * HTTP Protocol Version Constants + */ + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,33,0) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_2_0", CURL_HTTP_VERSION_2_0, CONST_CS|CONST_PERSISTENT); + #endif + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "HTTP_VERSION_ANY", CURL_HTTP_VERSION_NONE, CONST_CS|CONST_PERSISTENT); + + /* + * SSL Version Constants + */ + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1", CURL_SSLVERSION_TLSv1, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,34,0) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_0", CURL_SSLVERSION_TLSv1_0, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_1", CURL_SSLVERSION_TLSv1_1, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_TLSv1_2", CURL_SSLVERSION_TLSv1_2, CONST_CS|CONST_PERSISTENT); + #endif + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv2", CURL_SSLVERSION_SSLv2, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_SSLv3", CURL_SSLVERSION_SSLv3, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "SSL_VERSION_ANY", CURL_SSLVERSION_DEFAULT, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,21,4) && defined(PHP_HTTP_CURL_TLSAUTH_SRP) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "TLSAUTH_SRP", CURL_TLSAUTH_SRP, CONST_CS|CONST_PERSISTENT); + #endif + + /* + * DNS IPvX resolving + */ + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V4", CURL_IPRESOLVE_V4, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_V6", CURL_IPRESOLVE_V6, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "IPRESOLVE_ANY", CURL_IPRESOLVE_WHATEVER, CONST_CS|CONST_PERSISTENT); + + /* + * Auth Constants + */ + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_BASIC", CURLAUTH_BASIC, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST", CURLAUTH_DIGEST, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,19,3) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_DIGEST_IE", CURLAUTH_DIGEST_IE, CONST_CS|CONST_PERSISTENT); + #endif + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_NTLM", CURLAUTH_NTLM, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_GSSNEG", CURLAUTH_GSSNEGOTIATE, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,38,0) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_SPNEGO", CURLAUTH_NEGOTIATE, CONST_CS|CONST_PERSISTENT); + #endif + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "AUTH_ANY", CURLAUTH_ANY, CONST_CS|CONST_PERSISTENT); + + /* + * Proxy Type Constants + */ + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4", CURLPROXY_SOCKS4, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS4A", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5_HOSTNAME", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_SOCKS5", CURLPROXY_SOCKS5, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP", CURLPROXY_HTTP, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,19,4) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "PROXY_HTTP_1_0", CURLPROXY_HTTP_1_0, CONST_CS|CONST_PERSISTENT); + #endif + + /* + * Post Redirection Constants + */ + #if PHP_HTTP_CURL_VERSION(7,19,1) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_301", CURL_REDIR_POST_301, CONST_CS|CONST_PERSISTENT); + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_302", CURL_REDIR_POST_302, CONST_CS|CONST_PERSISTENT); + #if PHP_HTTP_CURL_VERSION(7,26,0) + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_303", CURL_REDIR_POST_303, CONST_CS|CONST_PERSISTENT); + #endif + REGISTER_NS_LONG_CONSTANT("http\\Client\\Curl", "POSTREDIR_ALL", CURL_REDIR_POST_ALL, CONST_CS|CONST_PERSISTENT); + #endif + + return SUCCESS; + } + + PHP_MSHUTDOWN_FUNCTION(http_client_curl) + { - php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl"), NULL, 0 TSRMLS_CC); - php_persistent_handle_cleanup(ZEND_STRL("http\\Client\\Curl\\Request"), NULL, 0 TSRMLS_CC); ++ php_persistent_handle_cleanup(PHP_HTTP_G->client.curl.driver.client_name, NULL); ++ php_persistent_handle_cleanup(PHP_HTTP_G->client.curl.driver.request_name, NULL); ++ zend_string_release(PHP_HTTP_G->client.curl.driver.client_name); ++ zend_string_release(PHP_HTTP_G->client.curl.driver.request_name); ++ zend_string_release(PHP_HTTP_G->client.curl.driver.driver_name); + + php_http_options_dtor(&php_http_curle_options); + php_http_options_dtor(&php_http_curlm_options); + + return SUCCESS; + } + + #endif /* PHP_HTTP_HAVE_CURL */ + + /* + * 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 --cc src/php_http_client_curl.h index 0000000,c82a09c..9128647 mode 000000,100644..100644 --- a/src/php_http_client_curl.h +++ b/src/php_http_client_curl.h @@@ -1,0 -1,31 +1,35 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_CLIENT_CURL_H + #define PHP_HTTP_CLIENT_CURL_H + + #if PHP_HTTP_HAVE_CURL + ++struct php_http_client_curl_globals { ++ php_http_client_driver_t driver; ++}; ++ + PHP_MINIT_FUNCTION(http_client_curl); + PHP_MSHUTDOWN_FUNCTION(http_client_curl); + #endif /* PHP_HTTP_HAVE_CURL */ + + #endif /* PHP_HTTP_CLIENT_CURL_H */ + + /* + * 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 --cc src/php_http_client_request.c index 0000000,0e40cc5..05d4a81 mode 000000,100644..100644 --- a/src/php_http_client_request.c +++ b/src/php_http_client_request.c @@@ -1,0 -1,314 +1,301 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC); -void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC); -void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value TSRMLS_DC); ++void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite); ++void php_http_client_options_set(zval *this_ptr, zval *opts); ++void php_http_client_options_get_subr(zval *this_ptr, char *key, size_t len, zval *return_value); + + #define PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj) \ + do { \ + if (!obj->message) { \ - obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC); \ ++ obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL); \ + } \ + } while(0) + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest___construct, 0, 0, 0) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, url) + ZEND_ARG_ARRAY_INFO(0, headers, 1) + ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, __construct) + { + char *meth_str = NULL; - int meth_len = 0; ++ size_t meth_len = 0; + zval *zheaders = NULL, *zbody = NULL, *zurl = NULL; + php_http_message_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!z!a!O!", &meth_str, &meth_len, &zurl, &zheaders, &zbody, php_http_message_body_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->message) { + php_http_message_set_type(obj->message, PHP_HTTP_REQUEST); + } else { - obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL TSRMLS_CC); ++ obj->message = php_http_message_init(NULL, PHP_HTTP_REQUEST, NULL); + } + + if (zbody) { - php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody TSRMLS_CC), unexpected_val, return); ++ php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody), unexpected_val, return); + } + if (meth_str && meth_len) { + PHP_HTTP_INFO(obj->message).request.method = estrndup(meth_str, meth_len); + } + if (zurl) { - PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC); ++ PHP_HTTP_INFO(obj->message).request.url = php_http_url_from_zval(zurl, ~0); + } + if (zheaders) { + array_copy(Z_ARRVAL_P(zheaders), &obj->message->hdrs); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setContentType, 0, 0, 1) + ZEND_ARG_INFO(0, content_type) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, setContentType) + { - char *ct_str; - int ct_len; ++ zend_string *ct_str; + php_http_message_object_t *obj; - zval *zct; ++ zval zct; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ct_str, &ct_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "S", &ct_str), invalid_arg, return); + - if (ct_len && !strchr(ct_str, '/')) { - php_http_throw(unexpected_val, "Content type \"%s\" does not seem to contain a primary and a secondary part", ct_str); ++ if (ct_str->len && !strchr(ct_str->val, '/')) { ++ php_http_throw(unexpected_val, "Content type \"%s\" does not seem to contain a primary and a secondary part", ct_str->val); + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj); + - MAKE_STD_ZVAL(zct); - ZVAL_STRINGL(zct, ct_str, ct_len, 1); - zend_hash_update(&obj->message->hdrs, "Content-Type", sizeof("Content-Type"), (void *) &zct, sizeof(void *), NULL); ++ ZVAL_STR_COPY(&zct, ct_str); ++ zend_hash_str_update(&obj->message->hdrs, "Content-Type", lenof("Content-Type"), &zct); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getContentType, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, getContentType) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + zval *zct; + + PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj); + + php_http_message_update_headers(obj->message); - zct = php_http_message_header(obj->message, ZEND_STRL("Content-Type"), 1); ++ zct = php_http_message_header(obj->message, ZEND_STRL("Content-Type")); + if (zct) { - RETURN_ZVAL(zct, 0, 1); ++ RETURN_ZVAL(zct, 1, 0); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setQuery, 0, 0, 0) + ZEND_ARG_INFO(0, query_data) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, setQuery) + { - zval *qdata = NULL; ++ zval *qdata = NULL, arr, str; + php_http_message_object_t *obj; + php_http_url_t *old_url = NULL, new_url = {NULL}; - char empty[] = ""; + unsigned flags = PHP_HTTP_URL_REPLACE; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &qdata), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!", &qdata), invalid_arg, return); + ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj); + ++ ZVAL_NULL(&str); + if (qdata) { - zval arr, str; - - INIT_PZVAL(&arr); + array_init(&arr); - INIT_PZVAL(&str); - ZVAL_NULL(&str); + - php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring, ++ php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str), bad_querystring, + zval_dtor(&arr); + return; + ); ++ + new_url.query = Z_STRVAL(str); + zval_dtor(&arr); + } else { + flags = PHP_HTTP_URL_STRIP_QUERY; + } + + if (obj->message->http.info.request.url) { + old_url = obj->message->http.info.request.url; + } + - obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags TSRMLS_CC); ++ obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, flags); + + if (old_url) { + php_http_url_free(&old_url); + } - if (new_url.query != &empty[0]) { - PTR_FREE(new_url.query); - } ++ zval_ptr_dtor(&str); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getQuery, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, getQuery) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj); + + if (obj->message->http.info.request.url && obj->message->http.info.request.url->query) { - RETVAL_STRING(obj->message->http.info.request.url->query, 1); ++ RETVAL_STRING(obj->message->http.info.request.url->query); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addQuery, 0, 0, 1) + ZEND_ARG_INFO(0, query_data) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, addQuery) + { + zval *qdata, arr, str; + php_http_message_object_t *obj; + php_http_url_t *old_url = NULL, new_url = {NULL}; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &qdata), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &qdata), invalid_arg, return); + ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_CLIENT_REQUEST_OBJECT_INIT(obj); + - INIT_PZVAL(&arr); + array_init(&arr); - INIT_PZVAL(&str); + ZVAL_NULL(&str); + - php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str TSRMLS_CC), bad_querystring, ++ php_http_expect(SUCCESS == php_http_querystring_update(&arr, qdata, &str), bad_querystring, + zval_dtor(&arr); + return; + ); + new_url.query = Z_STRVAL(str); + zval_dtor(&arr); + + if (obj->message->http.info.request.url) { + old_url = obj->message->http.info.request.url; + } + - obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC); ++ obj->message->http.info.request.url = php_http_url_mod(old_url, &new_url, PHP_HTTP_URL_JOIN_QUERY); + + if (old_url) { + php_http_url_free(&old_url); + } - PTR_FREE(new_url.query); ++ zval_ptr_dtor(&str); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, options, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, setOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set(getThis(), opts TSRMLS_CC); ++ php_http_client_options_set(getThis(), opts); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getOptions, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, getOptions) + { + if (SUCCESS == zend_parse_parameters_none()) { - zval *zoptions = zend_read_property(php_http_client_request_class_entry, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); ++ zval tmp, *zoptions = zend_read_property(php_http_client_request_class_entry, getThis(), ZEND_STRL("options"), 0, &tmp); + RETURN_ZVAL(zoptions, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_setSslOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, ssl_options, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, setSslOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts, 1); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_addSslOptions, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, ssl_options, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, addSslOptions) + { + zval *opts = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!/", &opts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|a!/", &opts), invalid_arg, return); + - php_http_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); ++ php_http_client_options_set_subr(getThis(), ZEND_STRL("ssl"), opts, 0); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientRequest_getSslOptions, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientRequest, getSslOptions) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_client_options_get_subr(getThis(), ZEND_STRS("ssl"), return_value TSRMLS_CC); ++ php_http_client_options_get_subr(getThis(), ZEND_STRL("ssl"), return_value); + } + } + + static zend_function_entry php_http_client_request_methods[] = { + PHP_ME(HttpClientRequest, __construct, ai_HttpClientRequest___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpClientRequest, setContentType, ai_HttpClientRequest_setContentType, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, getContentType, ai_HttpClientRequest_getContentType, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, setQuery, ai_HttpClientRequest_setQuery, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, getQuery, ai_HttpClientRequest_getQuery, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, addQuery, ai_HttpClientRequest_addQuery, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, setOptions, ai_HttpClientRequest_setOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, getOptions, ai_HttpClientRequest_getOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, setSslOptions, ai_HttpClientRequest_setSslOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, getSslOptions, ai_HttpClientRequest_getSslOptions, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientRequest, addSslOptions, ai_HttpClientRequest_addSslOptions, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_client_request_class_entry; + + PHP_MINIT_FUNCTION(http_client_request) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Request", php_http_client_request_methods); - php_http_client_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC); ++ php_http_client_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry); + - zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_client_response.c index 0000000,8d512ec..b1377fe mode 000000,100644..100644 --- a/src/php_http_client_response.c +++ b/src/php_http_client_response.c @@@ -1,0 -1,143 +1,146 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getCookies, 0, 0, 0) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, allowed_extras) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientResponse, getCookies) + { - long flags = 0; ++ zend_long flags = 0; + zval *allowed_extras_array = NULL; + int i = 0; + char **allowed_extras = NULL; - zval *header = NULL, **entry = NULL; - HashPosition pos; ++ zval *header = NULL, *entry = NULL; + php_http_message_object_t *msg; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|la!/", &flags, &allowed_extras_array)) { + return; + } + - msg = zend_object_store_get_object(getThis() TSRMLS_CC); ++ msg = PHP_HTTP_OBJ(NULL, getThis()); + array_init(return_value); + + if (allowed_extras_array) { ++ /* FIXME: use zend_string** instead of char** */ + allowed_extras = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(allowed_extras_array)) + 1, sizeof(char *)); - FOREACH_VAL(pos, allowed_extras_array, entry) { - zval *data = php_http_ztyp(IS_STRING, *entry); - allowed_extras[i++] = estrndup(Z_STRVAL_P(data), Z_STRLEN_P(data)); - zval_ptr_dtor(&data); ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(allowed_extras_array), entry) ++ { ++ zend_string *zs = zval_get_string(entry); ++ allowed_extras[i++] = estrndup(zs->val, zs->len); ++ zend_string_release(zs); + } ++ ZEND_HASH_FOREACH_END(); + } + - if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie"), 0))) { ++ if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie")))) { + php_http_cookie_list_t *list; + + if (Z_TYPE_P(header) == IS_ARRAY) { - zval **single_header; ++ zval *single_header; + - FOREACH_VAL(pos, header, single_header) { - zval *data = php_http_ztyp(IS_STRING, *single_header); ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), single_header) ++ { ++ zend_string *zs = zval_get_string(single_header); + - if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) { - zval *cookie; ++ if ((list = php_http_cookie_list_parse(NULL, zs->val, zs->len, flags, allowed_extras))) { ++ zval cookie; + - MAKE_STD_ZVAL(cookie); - ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0); - add_next_index_zval(return_value, cookie); ++ ZVAL_OBJ(&cookie, &php_http_cookie_object_new_ex(php_http_cookie_class_entry, list)->zo); ++ add_next_index_zval(return_value, &cookie); + } - zval_ptr_dtor(&data); ++ zend_string_release(zs); + } ++ ZEND_HASH_FOREACH_END(); + } else { - zval *data = php_http_ztyp(IS_STRING, header); - if ((list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(data), Z_STRLEN_P(data), flags, allowed_extras TSRMLS_CC))) { - zval *cookie; ++ zend_string *zs = zval_get_string(header); + - MAKE_STD_ZVAL(cookie); - ZVAL_OBJVAL(cookie, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0); - add_next_index_zval(return_value, cookie); ++ if ((list = php_http_cookie_list_parse(NULL, zs->val, zs->len, flags, allowed_extras))) { ++ zval cookie; ++ ++ ZVAL_OBJ(&cookie, &php_http_cookie_object_new_ex(php_http_cookie_class_entry, list)->zo); ++ add_next_index_zval(return_value, &cookie); + } - zval_ptr_dtor(&data); ++ zend_string_release(zs); + } - zval_ptr_dtor(&header); + } + + if (allowed_extras) { + for (i = 0; allowed_extras[i]; ++i) { + efree(allowed_extras[i]); + } + efree(allowed_extras); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpClientResponse_getTransferInfo, 0, 0, 0) + ZEND_ARG_INFO(0, element) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpClientResponse, getTransferInfo) + { + char *info_name = NULL; - int info_len = 0; - zval *info; ++ size_t info_len = 0; ++ zval info_tmp, info_name_tmp, *info; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &info_name, &info_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &info_name, &info_len), invalid_arg, return); + - info = zend_read_property(php_http_client_response_class_entry, getThis(), ZEND_STRL("transferInfo"), 0 TSRMLS_CC); ++ info = zend_read_property(php_http_client_response_class_entry, getThis(), ZEND_STRL("transferInfo"), 0, &info_tmp); + + /* request completed? */ + if (Z_TYPE_P(info) != IS_OBJECT) { + php_http_throw(bad_method_call, "Incomplete state", NULL); + return; + } + + if (info_len && info_name) { - info = zend_read_property(NULL, info, php_http_pretty_key(info_name, info_len, 0, 0), info_len, 0 TSRMLS_CC); ++ info = zend_read_property(NULL, info, php_http_pretty_key(info_name, info_len, 0, 0), info_len, 0, &info_name_tmp); + + if (!info) { + php_http_throw(unexpected_val, "Could not find transfer info with name '%s'", info_name); + return; + } + } + + RETURN_ZVAL(info, 1, 0); + } + + static zend_function_entry php_http_client_response_methods[] = { + PHP_ME(HttpClientResponse, getCookies, ai_HttpClientResponse_getCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpClientResponse, getTransferInfo, ai_HttpClientResponse_getTransferInfo, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_client_response_class_entry; + + PHP_MINIT_FUNCTION(http_client_response) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Client", "Response", php_http_client_response_methods); - php_http_client_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC); - zend_declare_property_null(php_http_client_response_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ php_http_client_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry); ++ ++ zend_declare_property_null(php_http_client_response_class_entry, ZEND_STRL("transferInfo"), ZEND_ACC_PROTECTED); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_cookie.c index 0000000,354dfa6..9625401 mode 000000,100644..100644 --- a/src/php_http_cookie.c +++ b/src/php_http_cookie.c @@@ -1,0 -1,1038 +1,1043 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC) ++php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list) + { + if (!list) { + list = emalloc(sizeof(*list)); + } + + zend_hash_init(&list->cookies, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&list->extras, 0, NULL, ZVAL_PTR_DTOR, 0); + + list->path = NULL; + list->domain = NULL; + list->expires = -1; + list->max_age = -1; + list->flags = 0; + - TSRMLS_SET_CTX(list->ts); - + return list; + } + + php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to) + { - TSRMLS_FETCH_FROM_CTX(from->ts); - - to = php_http_cookie_list_init(to TSRMLS_CC); ++ to = php_http_cookie_list_init(to); + + array_copy(&from->cookies, &to->cookies); + array_copy(&from->extras, &to->extras); + + PTR_SET(to->path, from->path ? estrdup(from->path) : NULL); + PTR_SET(to->domain, from->domain ? estrdup(from->domain) : NULL); + to->expires = from->expires; + to->max_age = from->max_age; + to->flags = from->flags; + + return to; + } + + void php_http_cookie_list_dtor(php_http_cookie_list_t *list) + { + if (list) { + zend_hash_destroy(&list->cookies); + zend_hash_destroy(&list->extras); + + PTR_SET(list->path, NULL); + PTR_SET(list->domain, NULL); + } + } + + + + void php_http_cookie_list_free(php_http_cookie_list_t **list) + { + if (*list) { + php_http_cookie_list_dtor(*list); + efree(*list); + *list = NULL; + } + } + -const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zcookie) ++const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval *zcookie) + { - zval **cookie; - if ((SUCCESS != zend_symtable_find(&list->cookies, name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) { ++ zval *cookie = zend_symtable_str_find(&list->cookies, name, name_len); ++ ++ if (!cookie || (Z_TYPE_P(cookie) != IS_STRING)) { + return NULL; + } + if (zcookie) { + *zcookie = *cookie; + } - return Z_STRVAL_PP(cookie); ++ return Z_STRVAL_P(cookie); + } + -const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **zextra) ++const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval *zextra) + { - zval **extra; ++ zval *extra = zend_symtable_str_find(&list->extras, name, name_len); + - if ((SUCCESS != zend_symtable_find(&list->extras, name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) { ++ if (!extra || (Z_TYPE_P(extra) != IS_STRING)) { + return NULL; + } + if (zextra) { + *zextra = *extra; + } - return Z_STRVAL_PP(extra); ++ return Z_STRVAL_P(extra); + } + + void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len) + { - zval *cookie_value; ++ zval cookie_value; + - MAKE_STD_ZVAL(cookie_value); - ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); - zend_symtable_update(&list->cookies, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); ++ ZVAL_STRINGL(&cookie_value, value, value_len); ++ zend_symtable_str_update(&list->cookies, name, name_len, &cookie_value); + } + + void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len) + { - zval *cookie_value; ++ zval extra_value; + - MAKE_STD_ZVAL(cookie_value); - ZVAL_STRINGL(cookie_value, estrndup(value, value_len), value_len, 0); - zend_symtable_update(&list->extras, name, name_len + 1, (void *) &cookie_value, sizeof(zval *), NULL); ++ ZVAL_STRINGL(&extra_value, value, value_len); ++ zend_symtable_str_update(&list->extras, name, name_len, &extra_value); + } + -#define _KEY_IS(s) (key->len == sizeof(s) && !strncasecmp(key->str, (s), key->len)) -static void add_entry(php_http_cookie_list_t *list, char **allowed_extras, long flags, php_http_array_hashkey_t *key, zval *val) ++#define _KEY_IS(s) (key->key && key->key->len == sizeof(s)-1 && !strncasecmp(key->key->val, (s), key->key->len)) ++static void add_entry(php_http_cookie_list_t *list, char **allowed_extras, long flags, zend_hash_key *key, zval *val) + { - zval *arg = php_http_zsep(1, IS_STRING, val); ++ zval arg; ++ ++ ZVAL_DUP(&arg, val); ++ convert_to_string(&arg); + + if (!(flags & PHP_HTTP_COOKIE_PARSE_RAW)) { - Z_STRLEN_P(arg) = php_raw_url_decode(Z_STRVAL_P(arg), Z_STRLEN_P(arg)); ++ Z_STRLEN(arg) = php_raw_url_decode(Z_STRVAL(arg), Z_STRLEN(arg)); ++ zend_string_forget_hash_val(Z_STR(arg)); + } + + if _KEY_IS("path") { - PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); ++ PTR_SET(list->path, estrndup(Z_STRVAL(arg), Z_STRLEN(arg))); + } else if _KEY_IS("domain") { - PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); ++ PTR_SET(list->domain, estrndup(Z_STRVAL(arg), Z_STRLEN(arg))); + } else if _KEY_IS("expires") { - char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg)); ++ char *date = estrndup(Z_STRVAL(arg), Z_STRLEN(arg)); + list->expires = php_parse_date(date, NULL); + efree(date); + } else if _KEY_IS("max-age") { - list->max_age = strtol(Z_STRVAL_P(arg), NULL, 10); ++ list->max_age = zval_get_long(val); + } else if _KEY_IS("secure") { + list->flags |= PHP_HTTP_COOKIE_SECURE; + } else if _KEY_IS("httpOnly") { + list->flags |= PHP_HTTP_COOKIE_HTTPONLY; + } else { ++ php_http_arrkey_t tmp = {0}; ++ ++ php_http_arrkey_stringify(&tmp, key); ++ + /* check for extra */ + if (allowed_extras) { + char **ae = allowed_extras; - - php_http_array_hashkey_stringify(key); + for (; *ae; ++ae) { - if (!strncasecmp(key->str, *ae, key->len)) { - if (key->type == HASH_KEY_IS_LONG) { - zend_hash_index_update(&list->extras, key->num, (void *) &arg, sizeof(zval *), NULL); - } else { - zend_hash_update(&list->extras, key->str, key->len, (void *) &arg, sizeof(zval *), NULL); - } - php_http_array_hashkey_stringfree(key); ++ if (!strncasecmp(*ae, tmp.key->val, tmp.key->len)) { ++ zend_symtable_update(&list->extras, tmp.key, &arg); ++ php_http_arrkey_dtor(&tmp); + return; + } + } - php_http_array_hashkey_stringfree(key); + } + + /* cookie */ - if (key->type == HASH_KEY_IS_LONG) { - zend_hash_index_update(&list->cookies, key->num, (void *) &arg, sizeof(zval *), NULL); - } else { - zend_hash_update(&list->cookies, key->str, key->len, (void *) &arg, sizeof(zval *), NULL); - } ++ zend_symtable_update(&list->cookies, tmp.key, &arg); ++ ++ php_http_arrkey_dtor(&tmp); + return; + } ++ + zval_ptr_dtor(&arg); + } + -php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC) ++php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras) + { + php_http_params_opts_t opts; + HashTable params; - HashPosition pos1, pos2; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **param, **val, **args, **arg; ++ zend_hash_key k, arg_k; ++ zval *param, *val, *args, *arg; + + php_http_params_opts_default_get(&opts); + opts.input.str = estrndup(str, len); + opts.input.len = len; + opts.param = NULL; + zend_hash_init(¶ms, 10, NULL, ZVAL_PTR_DTOR, 0); - php_http_params_parse(¶ms, &opts TSRMLS_CC); ++ php_http_params_parse(¶ms, &opts); + efree(opts.input.str); + - list = php_http_cookie_list_init(list TSRMLS_CC); - FOREACH_HASH_KEYVAL(pos1, ¶ms, key, param) { - if (Z_TYPE_PP(param) == IS_ARRAY) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("value"), (void *) &val)) { - add_entry(list, NULL, flags, &key, *val); ++ list = php_http_cookie_list_init(list); ++ ZEND_HASH_FOREACH_KEY_VAL(¶ms, k.h, k.key, param) ++ { ++ if (Z_TYPE_P(param) == IS_ARRAY) { ++ if ((val = zend_hash_str_find(Z_ARRVAL_P(param), ZEND_STRL("value")))) { ++ add_entry(list, NULL, flags, &k, val); + } - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(param), ZEND_STRS("arguments"), (void *) &args) && Z_TYPE_PP(args) == IS_ARRAY) { - FOREACH_KEYVAL(pos2, *args, key, arg) { - add_entry(list, allowed_extras, flags, &key, *arg); ++ if ((args = zend_hash_str_find(Z_ARRVAL_P(param), ZEND_STRL("arguments"))) && Z_TYPE_P(args) == IS_ARRAY) { ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(args), arg_k.h, arg_k.key, arg) ++ { ++ add_entry(list, allowed_extras, flags, &arg_k, arg); + } ++ ZEND_HASH_FOREACH_END(); + } + } + } ++ ZEND_HASH_FOREACH_END(); ++ + zend_hash_destroy(¶ms); + + return list; + } + + void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct) + { - zval array, *cookies, *extras; - TSRMLS_FETCH_FROM_CTX(list->ts); - - INIT_PZVAL_ARRAY(&array, HASH_OF(strct)); ++ zval cookies, extras, tmp; ++ HashTable *ht = HASH_OF(strct); + - MAKE_STD_ZVAL(cookies); - array_init(cookies); - zend_hash_copy(Z_ARRVAL_P(cookies), &list->cookies, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - add_assoc_zval(&array, "cookies", cookies); ++ array_init_size(&cookies, zend_hash_num_elements(&list->cookies)); ++ array_copy(&list->cookies, Z_ARRVAL(cookies)); ++ zend_symtable_str_update(ht, ZEND_STRL("cookies"), &cookies); + - MAKE_STD_ZVAL(extras); - array_init(extras); - zend_hash_copy(Z_ARRVAL_P(extras), &list->extras, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - add_assoc_zval(&array, "extras", extras); ++ array_init_size(&extras, zend_hash_num_elements(&list->extras)); ++ array_copy(&list->extras, Z_ARRVAL(extras)); ++ zend_symtable_str_update(ht, ZEND_STRL("extras"), &extras); + - add_assoc_long(&array, "flags", list->flags); - add_assoc_long(&array, "expires", (long) list->expires); - add_assoc_long(&array, "max-age", (long) list->max_age); - add_assoc_string(&array, "path", STR_PTR(list->path), 1); - add_assoc_string(&array, "domain", STR_PTR(list->domain), 1); ++ ZVAL_LONG(&tmp, list->flags); ++ zend_symtable_str_update(ht, ZEND_STRL("flags"), &tmp); ++ ZVAL_LONG(&tmp, list->expires); ++ zend_symtable_str_update(ht, ZEND_STRL("expires"), &tmp); ++ ZVAL_LONG(&tmp, list->max_age); ++ zend_symtable_str_update(ht, ZEND_STRL("max-age"), &tmp); ++ ZVAL_STRING(&tmp, STR_PTR(list->path)); ++ zend_symtable_str_update(ht, ZEND_STRL("path"), &tmp); ++ ZVAL_STRING(&tmp, STR_PTR(list->domain)); ++ zend_symtable_str_update(ht, ZEND_STRL("domain"), &tmp); + } + -php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC) ++php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct) + { - zval **tmp, *cpy; - HashTable *ht = HASH_OF(strct); - - list = php_http_cookie_list_init(list TSRMLS_CC); - - if (SUCCESS == zend_hash_find(ht, "cookies", sizeof("cookies"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) { - zend_hash_copy(&list->cookies, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ zval *tmp; ++ HashTable *ht; ++ ++ ht = HASH_OF(strct); ++ list = php_http_cookie_list_init(list); ++ ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("cookies"))) && Z_TYPE_P(tmp) == IS_ARRAY){ ++ array_copy(Z_ARRVAL_P(tmp), &list->cookies); + } - if (SUCCESS == zend_hash_find(ht, "extras", sizeof("extras"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_ARRAY) { - zend_hash_copy(&list->extras, Z_ARRVAL_PP(tmp), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("extras"))) && Z_TYPE_P(tmp) == IS_ARRAY){ ++ array_copy(Z_ARRVAL_P(tmp), &list->extras); + } - if (SUCCESS == zend_hash_find(ht, "flags", sizeof("flags"), (void *) &tmp)) { - cpy = php_http_ztyp(IS_LONG, *tmp); - list->flags = Z_LVAL_P(cpy); - zval_ptr_dtor(&cpy); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("flags")))) { ++ list->flags = zval_get_long(tmp); + } - if (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) { - if (Z_TYPE_PP(tmp) == IS_LONG) { - list->expires = Z_LVAL_PP(tmp); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("expires")))) { ++ if (Z_TYPE_P(tmp) == IS_LONG) { ++ list->expires = Z_LVAL_P(tmp); + } else { - long lval; ++ zend_long lval; ++ zend_string *lstr = zval_get_string(tmp); + - cpy = php_http_ztyp(IS_STRING, *tmp); - if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { ++ if (IS_LONG == is_numeric_string(lstr->val, lstr->len, &lval, NULL, 0)) { + list->expires = lval; + } else { - list->expires = php_parse_date(Z_STRVAL_P(cpy), NULL); ++ list->expires = php_parse_date(lstr->val, NULL); + } + - zval_ptr_dtor(&cpy); ++ zend_string_release(lstr); + } + } - if (SUCCESS == zend_hash_find(ht, "max-age", sizeof("max-age"), (void *) &tmp)) { - if (Z_TYPE_PP(tmp) == IS_LONG) { - list->max_age = Z_LVAL_PP(tmp); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("max-age")))) { ++ if (Z_TYPE_P(tmp) == IS_LONG) { ++ list->max_age = Z_LVAL_P(tmp); + } else { - long lval; ++ zend_long lval; ++ zend_string *lstr = zval_get_string(tmp); + - cpy = php_http_ztyp(IS_STRING, *tmp); - if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { ++ if (IS_LONG == is_numeric_string(lstr->val, lstr->len, &lval, NULL, 0)) { + list->max_age = lval; + } + - zval_ptr_dtor(&cpy); ++ zend_string_release(lstr); + } + } - if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) { - list->path = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("path")))) { ++ zend_string *str = zval_get_string(tmp); ++ ++ list->path = estrndup(str->val, str->len); ++ zend_string_release(str); + } - if (SUCCESS == zend_hash_find(ht, "domain", sizeof("domain"), (void *) &tmp) && Z_TYPE_PP(tmp) == IS_STRING) { - list->domain = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); ++ if ((tmp = zend_hash_str_find_ind(ht, ZEND_STRL("domain")))) { ++ zend_string *str = zval_get_string(tmp); ++ ++ list->domain = estrndup(str->val, str->len); ++ zend_string_release(str); + } + + return list; + } + + static inline void append_encoded(php_http_buffer_t *buf, const char *key, size_t key_len, const char *val, size_t val_len) + { - char *enc_str[2]; - int enc_len[2]; ++ zend_string *enc_str[2]; + - enc_str[0] = php_raw_url_encode(key, key_len, &enc_len[0]); - enc_str[1] = php_raw_url_encode(val, val_len, &enc_len[1]); ++ enc_str[0] = php_raw_url_encode(key, key_len); ++ enc_str[1] = php_raw_url_encode(val, val_len); + - php_http_buffer_append(buf, enc_str[0], enc_len[0]); ++ php_http_buffer_append(buf, enc_str[0]->val, enc_str[0]->len); + php_http_buffer_appends(buf, "="); - php_http_buffer_append(buf, enc_str[1], enc_len[1]); ++ php_http_buffer_append(buf, enc_str[1]->val, enc_str[1]->len); + php_http_buffer_appends(buf, "; "); + - efree(enc_str[0]); - efree(enc_str[1]); ++ zend_string_release(enc_str[0]); ++ zend_string_release(enc_str[1]); + } + + void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len) + { + php_http_buffer_t buf; - zval **val; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - HashPosition pos; - TSRMLS_FETCH_FROM_CTX(list->ts); ++ zend_hash_key key; ++ zval *val; + + php_http_buffer_init(&buf); - - FOREACH_HASH_KEYVAL(pos, &list->cookies, key, val) { - zval *tmp = php_http_ztyp(IS_STRING, *val); + - php_http_array_hashkey_stringify(&key); - append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - php_http_array_hashkey_stringfree(&key); ++ ZEND_HASH_FOREACH_KEY_VAL(&list->cookies, key.h, key.key, val) ++ { ++ zend_string *str = zval_get_string(val); ++ php_http_arrkey_t arrkey = {0}; + - zval_ptr_dtor(&tmp); ++ php_http_arrkey_stringify(&arrkey, &key); ++ append_encoded(&buf, arrkey.key->val, arrkey.key->len, str->val, str->len); ++ php_http_arrkey_dtor(&arrkey); ++ zend_string_release(str); + } ++ ZEND_HASH_FOREACH_END(); + + if (list->domain && *list->domain) { + php_http_buffer_appendf(&buf, "domain=%s; ", list->domain); + } + if (list->path && *list->path) { + php_http_buffer_appendf(&buf, "path=%s; ", list->path); + } + if (list->expires >= 0) { - char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), list->expires, 0 TSRMLS_CC); - php_http_buffer_appendf(&buf, "expires=%s; ", date); - efree(date); ++ zend_string *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), list->expires, 0); ++ php_http_buffer_appendf(&buf, "expires=%s; ", date->val); ++ zend_string_release(date); + } + if (list->max_age >= 0) { + php_http_buffer_appendf(&buf, "max-age=%ld; ", list->max_age); + } + - FOREACH_HASH_KEYVAL(pos, &list->extras, key, val) { - zval *tmp = php_http_ztyp(IS_STRING, *val); - - php_http_array_hashkey_stringify(&key); - append_encoded(&buf, key.str, key.len-1, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - php_http_array_hashkey_stringfree(&key); - - zval_ptr_dtor(&tmp); ++ ZEND_HASH_FOREACH_KEY_VAL(&list->extras, key.h, key.key, val) ++ { ++ zend_string *str = zval_get_string(val); ++ php_http_arrkey_t arrkey; ++ ++ php_http_arrkey_stringify(&arrkey, &key); ++ append_encoded(&buf, arrkey.key->val, arrkey.key->len, str->val, str->len); ++ php_http_arrkey_dtor(&arrkey); ++ zend_string_release(str); + } ++ ZEND_HASH_FOREACH_END(); + + if (list->flags & PHP_HTTP_COOKIE_SECURE) { + php_http_buffer_appends(&buf, "secure; "); + } + if (list->flags & PHP_HTTP_COOKIE_HTTPONLY) { + php_http_buffer_appends(&buf, "httpOnly; "); + } + + php_http_buffer_fix(&buf); + *str = buf.data; + *len = buf.used; + } + + + + static zend_object_handlers php_http_cookie_object_handlers; + -zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_cookie_object_new(zend_class_entry *ce) + { - return php_http_cookie_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_cookie_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **ptr TSRMLS_DC) ++php_http_cookie_object_t *php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list) + { + php_http_cookie_object_t *o; + - o = ecalloc(sizeof(*o), 1); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); ++ if (!ce) { ++ ce = php_http_cookie_class_entry; ++ } ++ ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); ++ o->zo.handlers = &php_http_cookie_object_handlers; + + if (list) { + o->list = list; + } + - if (ptr) { - *ptr = o; - } - - o->zv.handle = zend_objects_store_put(o, NULL, php_http_cookie_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_cookie_object_handlers; - - return o->zv; ++ return o; + } + + #define PHP_HTTP_COOKIE_OBJECT_INIT(obj) \ + do { \ + if (!obj->list) { \ - obj->list = php_http_cookie_list_init(NULL TSRMLS_CC); \ ++ obj->list = php_http_cookie_list_init(NULL); \ + } \ + } while(0) + -zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC) ++zend_object *php_http_cookie_object_clone(zval *obj) + { - php_http_cookie_object_t *new_obj, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - zend_object_value ov; ++ php_http_cookie_object_t *new_obj, *old_obj = PHP_HTTP_OBJ(NULL, obj); + + PHP_HTTP_COOKIE_OBJECT_INIT(old_obj); + - ov = php_http_cookie_object_new_ex(old_obj->zo.ce, php_http_cookie_list_copy(old_obj->list, NULL), &new_obj TSRMLS_CC); - zend_objects_clone_members((zend_object *) new_obj, ov, (zend_object *) old_obj, Z_OBJ_HANDLE_P(getThis()) TSRMLS_CC); ++ new_obj = php_http_cookie_object_new_ex(old_obj->zo.ce, php_http_cookie_list_copy(old_obj->list, NULL)); ++ zend_objects_clone_members(&new_obj->zo, &old_obj->zo); + - return ov; ++ return &new_obj->zo; + } + -void php_http_cookie_object_free(void *object TSRMLS_DC) ++void php_http_cookie_object_free(zend_object *object) + { - php_http_cookie_object_t *obj = object; ++ php_http_cookie_object_t *obj = PHP_HTTP_OBJ(object, NULL); + + php_http_cookie_list_free(&obj->list); - zend_object_std_dtor((zend_object *) obj TSRMLS_CC); - efree(obj); ++ zend_object_std_dtor(object); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie___construct, 0, 0, 0) + ZEND_ARG_INFO(0, cookie_string) + ZEND_ARG_INFO(0, parser_flags) + ZEND_ARG_INFO(0, allowed_extras) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, __construct) + { + php_http_cookie_object_t *obj; + zval *zcookie = NULL; + long flags = 0; + char **ae = NULL; + HashTable *allowed_extras = NULL; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!lH", &zcookie, &flags, &allowed_extras), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!lH", &zcookie, &flags, &allowed_extras), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh); + if (zcookie) { + + if (allowed_extras && zend_hash_num_elements(allowed_extras)) { + char **ae_ptr = safe_emalloc(zend_hash_num_elements(allowed_extras) + 1, sizeof(char *), 0); - HashPosition pos; - zval **val; ++ zval *val; + + ae = ae_ptr; - FOREACH_HASH_VAL(pos, allowed_extras, val) { - zval *cpy = php_http_ztyp(IS_STRING, *val); ++ ZEND_HASH_FOREACH_VAL(allowed_extras, val) ++ { ++ zend_string *str = zval_get_string(val); + - *ae_ptr++ = estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)); - zval_ptr_dtor(&cpy); ++ *ae_ptr++ = estrndup(str->val, str->len); ++ zend_string_release(str); + } ++ ZEND_HASH_FOREACH_END(); + *ae_ptr = NULL; + } + + switch (Z_TYPE_P(zcookie)) { + case IS_OBJECT: - if (instanceof_function(Z_OBJCE_P(zcookie), php_http_cookie_class_entry TSRMLS_CC)) { - php_http_cookie_object_t *zco = zend_object_store_get_object(zcookie TSRMLS_CC); ++ if (instanceof_function(Z_OBJCE_P(zcookie), php_http_cookie_class_entry)) { ++ php_http_cookie_object_t *zco = PHP_HTTP_OBJ(NULL, zcookie); + + if (zco->list) { + obj->list = php_http_cookie_list_copy(zco->list, NULL); + } + break; + } + /* no break */ + case IS_ARRAY: - obj->list = php_http_cookie_list_from_struct(obj->list, zcookie TSRMLS_CC); ++ obj->list = php_http_cookie_list_from_struct(obj->list, zcookie); + break; + default: { - zval *cpy = php_http_ztyp(IS_STRING, zcookie); ++ zend_string *str = zval_get_string(zcookie); + - obj->list = php_http_cookie_list_parse(obj->list, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), flags, ae TSRMLS_CC); - zval_ptr_dtor(&cpy); ++ obj->list = php_http_cookie_list_parse(obj->list, str->val, str->len, flags, ae); ++ zend_string_release(str); + break; + } + } + + if (ae) { + char **ae_ptr; + + for (ae_ptr = ae; *ae_ptr; ++ae_ptr) { + efree(*ae_ptr); + } + efree(ae); + } + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookies, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, getCookies) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + - array_init(return_value); ++ array_init_size(return_value, zend_hash_num_elements(&obj->list->cookies)); + array_copy(&obj->list->cookies, Z_ARRVAL_P(return_value)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookies, 0, 0, 0) + ZEND_ARG_INFO(0, cookies) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setCookies) + { + HashTable *cookies = NULL; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &cookies), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &cookies), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + zend_hash_clean(&obj->list->cookies); + if (cookies) { + array_copy_strings(cookies, &obj->list->cookies); + } + - RETVAL_ZVAL(getThis(), 1, 0); ++ RETURN_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookies, 0, 0, 1) + ZEND_ARG_INFO(0, cookies) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, addCookies) + { + HashTable *cookies = NULL; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &cookies), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "H", &cookies), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + array_join(cookies, &obj->list->cookies, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtras, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, getExtras) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + - array_init(return_value); ++ array_init_size(return_value, zend_hash_num_elements(&obj->list->extras)); + array_copy(&obj->list->extras, Z_ARRVAL_P(return_value)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtras, 0, 0, 0) + ZEND_ARG_INFO(0, extras) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setExtras) + { + HashTable *extras = NULL; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H", &extras), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &extras), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + zend_hash_clean(&obj->list->extras); + if (extras) { + array_copy_strings(extras, &obj->list->extras); + } + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtras, 0, 0, 1) + ZEND_ARG_INFO(0, extras) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, addExtras) + { + HashTable *extras = NULL; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &extras), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "H", &extras), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + array_join(extras, &obj->list->extras, 1, ARRAY_JOIN_STRONLY|ARRAY_JOIN_STRINGIFY); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getCookie, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, getCookie) + { + char *name_str; - int name_len; - zval *zvalue; ++ size_t name_len; ++ zval zvalue; + php_http_cookie_object_t *obj; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len)) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (php_http_cookie_list_get_cookie(obj->list, name_str, name_len, &zvalue)) { - RETURN_ZVAL(zvalue, 1, 0); ++ RETURN_ZVAL(&zvalue, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setCookie, 0, 0, 1) + ZEND_ARG_INFO(0, cookie_name) + ZEND_ARG_INFO(0, cookie_value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setCookie) + { + char *name_str, *value_str = NULL; - int name_len, value_len = 0; ++ size_t name_len, value_len = 0; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (!value_str) { + php_http_cookie_list_del_cookie(obj->list, name_str, name_len); + } else { + php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len); + } + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addCookie, 0, 0, 2) + ZEND_ARG_INFO(0, cookie_name) + ZEND_ARG_INFO(0, cookie_value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, addCookie) + { + char *name_str, *value_str; - int name_len, value_len; ++ size_t name_len, value_len; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + php_http_cookie_list_add_cookie(obj->list, name_str, name_len, value_str, value_len); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExtra, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, getExtra) + { + char *name_str; - int name_len; - zval *zvalue; ++ size_t name_len; ++ zval zvalue; + php_http_cookie_object_t *obj; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len)) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (php_http_cookie_list_get_extra(obj->list, name_str, name_len, &zvalue)) { - RETURN_ZVAL(zvalue, 1, 0); ++ RETURN_ZVAL(&zvalue, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExtra, 0, 0, 1) + ZEND_ARG_INFO(0, extra_name) + ZEND_ARG_INFO(0, extra_value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setExtra) + { + char *name_str, *value_str = NULL; - int name_len, value_len = 0; ++ size_t name_len, value_len = 0; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (!value_str) { + php_http_cookie_list_del_extra(obj->list, name_str, name_len); + } else { + php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len); + } + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_addExtra, 0, 0, 2) + ZEND_ARG_INFO(0, extra_name) + ZEND_ARG_INFO(0, extra_value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, addExtra) + { + char *name_str, *value_str; - int name_len, value_len; ++ size_t name_len, value_len; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + php_http_cookie_list_add_extra(obj->list, name_str, name_len, value_str, value_len); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getDomain, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, getDomain) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (obj->list->domain) { - RETURN_STRING(obj->list->domain, 1); ++ RETURN_STRING(obj->list->domain); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setDomain, 0, 0, 0) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setDomain) + { + char *domain_str = NULL; - int domain_len = 0; ++ size_t domain_len = 0; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &domain_str, &domain_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &domain_str, &domain_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + PTR_SET(obj->list->domain, domain_str ? estrndup(domain_str, domain_len) : NULL); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getPath, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, getPath) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (obj->list->path) { - RETURN_STRING(obj->list->path, 1); ++ RETURN_STRING(obj->list->path); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setPath, 0, 0, 0) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setPath) + { + char *path_str = NULL; - int path_len = 0; ++ size_t path_len = 0; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &path_str, &path_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &path_str, &path_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + PTR_SET(obj->list->path, path_str ? estrndup(path_str, path_len) : NULL); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getExpires, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, getExpires) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + RETURN_LONG(obj->list->expires); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setExpires, 0, 0, 0) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setExpires) + { + long ts = -1; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &ts), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + obj->list->expires = ts; + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getMaxAge, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, getMaxAge) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + RETURN_LONG(obj->list->max_age); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setMaxAge, 0, 0, 0) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setMaxAge) + { - long ts = -1; ++ long ma = -1; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &ma), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + - obj->list->max_age = ts; ++ obj->list->max_age = ma; + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_getFlags, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, getFlags) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + RETURN_LONG(obj->list->flags); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_setFlags, 0, 0, 0) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpCookie, setFlags) + { + long flags = 0; + php_http_cookie_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + obj->list->flags = flags; + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toString, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, toString) + { + php_http_cookie_object_t *obj; + char *str; + size_t len; + + if (SUCCESS != zend_parse_parameters_none()) { + RETURN_EMPTY_STRING(); + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + php_http_cookie_list_to_string(obj->list, &str, &len); + - RETURN_STRINGL(str, len, 0); ++ RETURN_NEW_STR(php_http_cs2zs(str, len)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpCookie_toArray, 0, 0, 0) + ZEND_END_ARG_INFO();; + static PHP_METHOD(HttpCookie, toArray) + { + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + - array_init(return_value); ++ array_init_size(return_value, 8); + php_http_cookie_list_to_struct(obj->list, return_value); + } + + static zend_function_entry php_http_cookie_methods[] = { + PHP_ME(HttpCookie, __construct, ai_HttpCookie___construct, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getCookies, ai_HttpCookie_getCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setCookies, ai_HttpCookie_setCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, addCookies, ai_HttpCookie_addCookies, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getCookie, ai_HttpCookie_getCookie, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setCookie, ai_HttpCookie_setCookie, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, addCookie, ai_HttpCookie_addCookie, ZEND_ACC_PUBLIC) + + PHP_ME(HttpCookie, getExtras, ai_HttpCookie_getExtras, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setExtras, ai_HttpCookie_setExtras, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, addExtras, ai_HttpCookie_addExtras, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getExtra, ai_HttpCookie_getExtra, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setExtra, ai_HttpCookie_setExtra, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, addExtra, ai_HttpCookie_addExtra, ZEND_ACC_PUBLIC) + + PHP_ME(HttpCookie, getDomain, ai_HttpCookie_getDomain, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setDomain, ai_HttpCookie_setDomain, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getPath, ai_HttpCookie_getPath, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setPath, ai_HttpCookie_setPath, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getExpires, ai_HttpCookie_getExpires, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setExpires, ai_HttpCookie_setExpires, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getMaxAge, ai_HttpCookie_getMaxAge, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setMaxAge, ai_HttpCookie_setMaxAge, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, getFlags, ai_HttpCookie_getFlags, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, setFlags, ai_HttpCookie_setFlags, ZEND_ACC_PUBLIC) + + PHP_ME(HttpCookie, toArray, ai_HttpCookie_toArray, ZEND_ACC_PUBLIC) + PHP_ME(HttpCookie, toString, ai_HttpCookie_toString, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpCookie, __toString, toString, ai_HttpCookie_toString, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_cookie_class_entry; + + PHP_MINIT_FUNCTION(http_cookie) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Cookie", php_http_cookie_methods); - php_http_cookie_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_cookie_class_entry = zend_register_internal_class(&ce); + php_http_cookie_class_entry->create_object = php_http_cookie_object_new; + memcpy(&php_http_cookie_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ++ php_http_cookie_object_handlers.offset = XtOffsetOf(php_http_cookie_object_t, zo); + php_http_cookie_object_handlers.clone_obj = php_http_cookie_object_clone; ++ php_http_cookie_object_handlers.free_obj = php_http_cookie_object_free; + - zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_COOKIE_PARSE_RAW TSRMLS_CC); - zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("SECURE"), PHP_HTTP_COOKIE_SECURE TSRMLS_CC); - zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("HTTPONLY"), PHP_HTTP_COOKIE_HTTPONLY TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_COOKIE_PARSE_RAW); ++ zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("SECURE"), PHP_HTTP_COOKIE_SECURE); ++ zend_declare_class_constant_long(php_http_cookie_class_entry, ZEND_STRL("HTTPONLY"), PHP_HTTP_COOKIE_HTTPONLY); + + return SUCCESS; + } + + + + /* + * 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 --cc src/php_http_cookie.h index 0000000,7cf00fe..f7dbf8d mode 000000,100644..100644 --- a/src/php_http_cookie.h +++ b/src/php_http_cookie.h @@@ -1,0 -1,84 +1,79 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_COOKIE_H + #define PHP_HTTP_COOKIE_H + + #define PHP_HTTP_COOKIE_SECURE 0x10L + #define PHP_HTTP_COOKIE_HTTPONLY 0x20L + + #define PHP_HTTP_COOKIE_PARSE_RAW 0x01L + + /* + generally a netscape cookie compliant struct, recognizing httpOnly attribute, too; + cookie params like those from rfc2109 and rfc2965 are just put into extras, if + one specifies them in allowed extras, else they're treated like cookies themself + */ + typedef struct php_http_cookie_list { + HashTable cookies; + HashTable extras; + long flags; + char *path; + char *domain; + time_t expires; + time_t max_age; - -#ifdef ZTS - void ***ts; -#endif + } php_http_cookie_list_t; + -PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list TSRMLS_DC); -PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras TSRMLS_DC); ++PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_init(php_http_cookie_list_t *list); ++PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_parse(php_http_cookie_list_t *list, const char *str, size_t len, long flags, char **allowed_extras); + PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_copy(php_http_cookie_list_t *from, php_http_cookie_list_t *to); + PHP_HTTP_API void php_http_cookie_list_dtor(php_http_cookie_list_t *list); + PHP_HTTP_API void php_http_cookie_list_free(php_http_cookie_list_t **list); + -#define php_http_cookie_list_has_cookie(list, name, name_len) zend_symtable_exists(&(list)->cookies, (name), (name_len)+1) -#define php_http_cookie_list_del_cookie(list, name, name_len) zend_symtable_del(&(list)->cookies, (name), (name_len)+1) ++#define php_http_cookie_list_has_cookie(list, name, name_len) zend_symtable_str_exists(&(list)->cookies, (name), (name_len)) ++#define php_http_cookie_list_del_cookie(list, name, name_len) zend_symtable_str_del(&(list)->cookies, (name), (name_len)) + PHP_HTTP_API void php_http_cookie_list_add_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len); -PHP_HTTP_API const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **cookie); ++PHP_HTTP_API const char *php_http_cookie_list_get_cookie(php_http_cookie_list_t *list, const char *name, size_t name_len, zval *cookie); + -#define php_http_cookie_list_has_extra(list, name, name_len) zend_symtable_exists(&(list)->extras, (name), (name_len)+1) -#define php_http_cookie_list_del_extra(list, name, name_len) zend_symtable_del(&(list)->extras, (name), (name_len)+1) ++#define php_http_cookie_list_has_extra(list, name, name_len) zend_symtable_str_exists(&(list)->extras, (name), (name_len)) ++#define php_http_cookie_list_del_extra(list, name, name_len) zend_symtable_str_del(&(list)->extras, (name), (name_len)) + PHP_HTTP_API void php_http_cookie_list_add_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, const char *value, size_t value_len); -PHP_HTTP_API const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval **extra); ++PHP_HTTP_API const char *php_http_cookie_list_get_extra(php_http_cookie_list_t *list, const char *name, size_t name_len, zval *extra); + + PHP_HTTP_API void php_http_cookie_list_to_string(php_http_cookie_list_t *list, char **str, size_t *len); -PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC); ++PHP_HTTP_API php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct); + PHP_HTTP_API void php_http_cookie_list_to_struct(php_http_cookie_list_t *list, zval *strct); + + PHP_HTTP_API zend_class_entry *php_http_cookie_class_entry; + + typedef struct php_http_cookie_object { - zend_object zo; - zend_object_value zv; + php_http_cookie_list_t *list; ++ zend_object zo; + } php_http_cookie_object_t; + -zend_object_value php_http_cookie_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list, php_http_cookie_object_t **obj TSRMLS_DC); -zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC); -void php_http_cookie_object_free(void *object TSRMLS_DC); ++zend_object *php_http_cookie_object_new(zend_class_entry *ce); ++php_http_cookie_object_t *php_http_cookie_object_new_ex(zend_class_entry *ce, php_http_cookie_list_t *list); ++zend_object *php_http_cookie_object_clone(zval *this_ptr); ++void php_http_cookie_object_free(zend_object *object); + + PHP_MINIT_FUNCTION(http_cookie); + + #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 --cc src/php_http_encoding.c index 0000000,286f2b5..e7557a0 mode 000000,100644..100644 --- a/src/php_http_encoding.c +++ b/src/php_http_encoding.c @@@ -1,0 -1,1227 +1,1229 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + + static inline int eol_match(char **line, int *eol_len) + { + char *ptr = *line; + + while (' ' == *ptr) ++ptr; + + if (ptr == php_http_locate_eol(*line, eol_len)) { + *line = ptr; + return 1; + } else { + return 0; + } + } + -const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC) ++const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len) + { + int eol_len = 0; + char *n_ptr = NULL; + const char *e_ptr = encoded; + + *decoded_len = 0; + *decoded = ecalloc(1, encoded_len + 1); + + while ((encoded + encoded_len - e_ptr) > 0) { + ulong chunk_len = 0, rest; + + chunk_len = strtoul(e_ptr, &n_ptr, 16); + + /* we could not read in chunk size */ + if (n_ptr == e_ptr) { + /* + * if this is the first turn and there doesn't seem to be a chunk + * size at the begining of the body, do not fail on apparently + * not encoded data and return a copy + */ + if (e_ptr == encoded) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Data does not seem to be chunked encoded"); ++ php_error_docref(NULL, E_NOTICE, "Data does not seem to be chunked encoded"); + memcpy(*decoded, encoded, encoded_len); + *decoded_len = encoded_len; + return encoded + encoded_len; + } else { + efree(*decoded); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len); ++ php_error_docref(NULL, E_WARNING, "Expected chunk size at pos %tu of %zu but got trash", n_ptr - encoded, encoded_len); + return NULL; + } + } + + /* reached the end */ + if (!chunk_len) { + /* move over '0' chunked encoding terminator and any new lines */ + do { + switch (*e_ptr) { + case '0': + case '\r': + case '\n': + ++e_ptr; + continue; + } + } while (0); + break; + } + + /* there should be CRLF after the chunk size, but we'll ignore SP+ too */ + if (*n_ptr && !eol_match(&n_ptr, &eol_len)) { + if (eol_len == 2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1)); ++ php_error_docref(NULL, E_WARNING, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr - encoded, encoded_len, *n_ptr, *(n_ptr + 1)); + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr); ++ php_error_docref(NULL, E_WARNING, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr - encoded, encoded_len, *n_ptr); + } + } + n_ptr += eol_len; + + /* chunk size pretends more data than we actually got, so it's probably a truncated message */ + if (chunk_len > (rest = encoded + encoded_len - n_ptr)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len); ++ php_error_docref(NULL, E_WARNING, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len, rest, n_ptr - encoded, encoded_len); + chunk_len = rest; + } + + /* copy the chunk */ + memcpy(*decoded + *decoded_len, n_ptr, chunk_len); + *decoded_len += chunk_len; + + if (chunk_len == rest) { + e_ptr = n_ptr + chunk_len; + break; + } else { + /* advance to next chunk */ + e_ptr = n_ptr + chunk_len + eol_len; + } + } + + return e_ptr; + } + + static inline int php_http_inflate_rounds(z_stream *Z, int flush, char **buf, size_t *len) + { + int status = 0, round = 0; + php_http_buffer_t buffer; + + *buf = NULL; + *len = 0; + + php_http_buffer_init_ex(&buffer, Z->avail_in, PHP_HTTP_BUFFER_INIT_PREALLOC); + + do { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_resize_ex(&buffer, buffer.size, 0, 1)) { + status = Z_MEM_ERROR; + } else { + Z->avail_out = buffer.free; + Z->next_out = (Bytef *) buffer.data + buffer.used; + #if 0 + fprintf(stderr, "\n%3d: %3d PRIOR: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); + #endif + status = inflate(Z, flush); + php_http_buffer_account(&buffer, buffer.free - Z->avail_out); + #if 0 + fprintf(stderr, "%3d: %3d AFTER: size=%7lu,\tfree=%7lu,\tused=%7lu,\tavail_in=%7lu,\tavail_out=%7lu\n", round, status, buffer.size, buffer.free, buffer.used, Z->avail_in, Z->avail_out); + #endif + PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(buffer.size); + } + } while ((Z_BUF_ERROR == status || (Z_OK == status && Z->avail_in)) && ++round < PHP_HTTP_INFLATE_ROUNDS); + + if (status == Z_OK || status == Z_STREAM_END) { + php_http_buffer_shrink(&buffer); + php_http_buffer_fix(&buffer); + *buf = buffer.data; + *len = buffer.used; + } else { + php_http_buffer_dtor(&buffer); + } + + return status; + } + -ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len TSRMLS_DC) ++ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len) + { + int status, level, wbits, strategy; + z_stream Z; + + PHP_HTTP_DEFLATE_LEVEL_SET(flags, level); + PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits); + PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy); + + memset(&Z, 0, sizeof(z_stream)); + *encoded = NULL; + *encoded_len = 0; + + status = deflateInit2(&Z, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy); + if (Z_OK == status) { + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc(*encoded_len); + + Z.next_in = (Bytef *) data; + Z.next_out = (Bytef *) *encoded; + Z.avail_in = data_len; + Z.avail_out = *encoded_len; + + status = deflate(&Z, Z_FINISH); + deflateEnd(&Z); + + if (Z_STREAM_END == status) { + /* size buffer down to actual length */ + *encoded = erealloc(*encoded, Z.total_out + 1); + (*encoded)[*encoded_len = Z.total_out] = '\0'; + return SUCCESS; + } else { + PTR_SET(*encoded, NULL); + *encoded_len = 0; + } + } + - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not deflate data: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Could not deflate data: %s", zError(status)); + return FAILURE; + } + -ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC) ++ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len) + { + z_stream Z; + int status, wbits = PHP_HTTP_WINDOW_BITS_ANY; + + memset(&Z, 0, sizeof(z_stream)); + + retry_raw_inflate: + status = inflateInit2(&Z, wbits); + if (Z_OK == status) { + Z.next_in = (Bytef *) data; + Z.avail_in = data_len + 1; /* include the terminating NULL, see #61287 */ + + switch (status = php_http_inflate_rounds(&Z, Z_NO_FLUSH, decoded, decoded_len)) { + case Z_STREAM_END: + inflateEnd(&Z); + return SUCCESS; + + case Z_OK: + status = Z_DATA_ERROR; + break; + + case Z_DATA_ERROR: + /* raw deflated data? */ + if (PHP_HTTP_WINDOW_BITS_ANY == wbits) { + inflateEnd(&Z); + wbits = PHP_HTTP_WINDOW_BITS_RAW; + goto retry_raw_inflate; + } + break; + } + inflateEnd(&Z); + + if (decoded_len && *decoded) { + efree(*decoded); + } + } + - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not inflate data: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Could not inflate data: %s", zError(status)); + return FAILURE; + } + -php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags TSRMLS_DC) ++php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags) + { + int freeme; + + if ((freeme = !s)) { + s = pemalloc(sizeof(*s), (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + } + memset(s, 0, sizeof(*s)); + + s->flags = flags; - TSRMLS_SET_CTX(s->ts); + + if ((s->ops = ops)) { + php_http_encoding_stream_t *ss = s->ops->init(s); + + if (ss) { + return ss; + } + } else { + return s; + } + + if (freeme) { + pefree(s, (flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + } + return NULL; + } + + php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) + { - TSRMLS_FETCH_FROM_CTX(from->ts); - + if (from->ops->copy) { + int freeme; + php_http_encoding_stream_t *ns; + + if ((freeme = !to)) { + to = pemalloc(sizeof(*to), (from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + } + memset(to, 0, sizeof(*to)); + + to->flags = from->flags; + to->ops = from->ops; - TSRMLS_SET_CTX(to->ts); + + if ((ns = to->ops->copy(from, to))) { + return ns; + } else { + return to; + } + + if (freeme) { + pefree(to, (to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + } + } + + return NULL; + } + + ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s) + { + php_http_encoding_stream_t *ss; ++ + if ((*s)->ops->dtor) { + (*s)->ops->dtor(*s); + } + if ((ss = (*s)->ops->init(*s))) { + *s = ss; + return SUCCESS; + } + return FAILURE; + } + + ZEND_RESULT_CODE php_http_encoding_stream_update(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len) + { + if (!s->ops->update) { + return FAILURE; + } + return s->ops->update(s, in_str, in_len, out_str, out_len); + } + + ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *out_len) + { + if (!s->ops->flush) { + *out_str = NULL; + *out_len = 0; + return SUCCESS; + } + return s->ops->flush(s, out_str, out_len); + } + + zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s) + { + if (!s->ops->done) { + return 0; + } + return s->ops->done(s); + } + + ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *out_len) + { + if (!s->ops->finish) { + *out_str = NULL; + *out_len = 0; + return SUCCESS; + } + return s->ops->finish(s, out_str, out_len); + } + + void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s) + { + if (s->ops->dtor) { + s->ops->dtor(s); + } + } + + void php_http_encoding_stream_free(php_http_encoding_stream_t **s) + { + if (*s) { + if ((*s)->ops->dtor) { + (*s)->ops->dtor(*s); + } + pefree(*s, ((*s)->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + *s = NULL; + } + } + + struct dechunk_ctx { + php_http_buffer_t buffer; + ulong hexlen; + unsigned zeroed:1; + }; + + static php_http_encoding_stream_t *deflate_init(php_http_encoding_stream_t *s) + { + int status, level, wbits, strategy, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); + z_streamp ctx = pecalloc(1, sizeof(z_stream), p); - TSRMLS_FETCH_FROM_CTX(s->ts); + + PHP_HTTP_DEFLATE_LEVEL_SET(s->flags, level); + PHP_HTTP_DEFLATE_WBITS_SET(s->flags, wbits); + PHP_HTTP_DEFLATE_STRATEGY_SET(s->flags, strategy); + + if (Z_OK == (status = deflateInit2(ctx, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, strategy))) { + if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + s->ctx = ctx; + return s; + } + deflateEnd(ctx); + status = Z_MEM_ERROR; + } + pefree(ctx, p); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to initialize deflate encoding stream: %s", zError(status)); + return NULL; + } + + static php_http_encoding_stream_t *inflate_init(php_http_encoding_stream_t *s) + { + int status, wbits, p = (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT); + z_streamp ctx = pecalloc(1, sizeof(z_stream), p); - TSRMLS_FETCH_FROM_CTX(s->ts); + + PHP_HTTP_INFLATE_WBITS_SET(s->flags, wbits); + + if (Z_OK == (status = inflateInit2(ctx, wbits))) { + if ((ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + s->ctx = ctx; + return s; + } + inflateEnd(ctx); + status = Z_MEM_ERROR; + } + pefree(ctx, p); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to initialize inflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to initialize inflate stream: %s", zError(status)); + return NULL; + } + + static php_http_encoding_stream_t *dechunk_init(php_http_encoding_stream_t *s) + { + struct dechunk_ctx *ctx = pecalloc(1, sizeof(*ctx), (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + + if (!php_http_buffer_init_ex(&ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT) ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) { + return NULL; + } + + ctx->hexlen = 0; + ctx->zeroed = 0; + s->ctx = ctx; + + return s; + } + + static php_http_encoding_stream_t *deflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) + { + int status, p = to->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; + z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); - TSRMLS_FETCH_FROM_CTX(from->ts); + + if (Z_OK == (status = deflateCopy(to_ctx, from_ctx))) { + if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); + to->ctx = to_ctx; + return to; + } + deflateEnd(to_ctx); + status = Z_MEM_ERROR; + } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to copy deflate encoding stream: %s", zError(status)); + return NULL; + } + + static php_http_encoding_stream_t *inflate_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) + { + int status, p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; + z_streamp from_ctx = from->ctx, to_ctx = pecalloc(1, sizeof(*to_ctx), p); - TSRMLS_FETCH_FROM_CTX(from->ts); + + if (Z_OK == (status = inflateCopy(to_ctx, from_ctx))) { + if ((to_ctx->opaque = php_http_buffer_init_ex(NULL, PHP_HTTP_DEFLATE_BUFFER_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0))) { + php_http_buffer_append(to_ctx->opaque, PHP_HTTP_BUFFER(from_ctx->opaque)->data, PHP_HTTP_BUFFER(from_ctx->opaque)->used); + to->ctx = to_ctx; + return to; + } + inflateEnd(to_ctx); + status = Z_MEM_ERROR; + } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: %s", zError(status)); + return NULL; + } + + static php_http_encoding_stream_t *dechunk_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to) + { + int p = from->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT; + struct dechunk_ctx *from_ctx = from->ctx, *to_ctx = pemalloc(sizeof(*to_ctx), p); - TSRMLS_FETCH_FROM_CTX(from->ts); + + if (php_http_buffer_init_ex(&to_ctx->buffer, PHP_HTTP_BUFFER_DEFAULT_SIZE, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0)) { + to_ctx->hexlen = from_ctx->hexlen; + to_ctx->zeroed = from_ctx->zeroed; + php_http_buffer_append(&to_ctx->buffer, from_ctx->buffer.data, from_ctx->buffer.used); + to->ctx = to_ctx; + return to; + } + pefree(to_ctx, p); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to copy inflate encoding stream: out of memory"); ++ php_error_docref(NULL, E_WARNING, "Failed to copy inflate encoding stream: out of memory"); + return NULL; + } + + static ZEND_RESULT_CODE deflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **encoded, size_t *encoded_len) + { + int status; + z_streamp ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + /* append input to our buffer */ + php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); + + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + /* deflate */ + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(data_len); + *encoded = emalloc(*encoded_len); + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + switch (status = deflate(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags))) { + case Z_OK: + case Z_STREAM_END: + /* cut processed chunk off the buffer */ + if (ctx->avail_in) { + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + } else { + php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); + } + + /* size buffer down to actual size */ + *encoded_len -= ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to update deflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to update deflate stream: %s", zError(status)); + return FAILURE; + } + + static ZEND_RESULT_CODE inflate_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len) + { + int status; + z_streamp ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + /* append input to buffer */ + php_http_buffer_append(PHP_HTTP_BUFFER(ctx->opaque), data, data_len); + + retry_raw_inflate: + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + switch (status = php_http_inflate_rounds(ctx, PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(s->flags), decoded, decoded_len)) { + case Z_OK: + case Z_STREAM_END: + /* cut off */ + if (ctx->avail_in) { + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + } else { + php_http_buffer_reset(PHP_HTTP_BUFFER(ctx->opaque)); + } + return SUCCESS; + + case Z_DATA_ERROR: + /* raw deflated data ? */ + if (!(s->flags & PHP_HTTP_INFLATE_TYPE_RAW) && !ctx->total_out) { + inflateEnd(ctx); + s->flags |= PHP_HTTP_INFLATE_TYPE_RAW; + inflateInit2(ctx, PHP_HTTP_WINDOW_BITS_RAW); + goto retry_raw_inflate; + } + break; + } + - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to update inflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to update inflate stream: %s", zError(status)); + return FAILURE; + } + + static ZEND_RESULT_CODE dechunk_update(php_http_encoding_stream_t *s, const char *data, size_t data_len, char **decoded, size_t *decoded_len) + { + php_http_buffer_t tmp; + struct dechunk_ctx *ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + if (ctx->zeroed) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Dechunk encoding stream has already reached the end of chunked input"); ++ php_error_docref(NULL, E_WARNING, "Dechunk encoding stream has already reached the end of chunked input"); + return FAILURE; + } + if ((PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(&ctx->buffer, data, data_len)) || !php_http_buffer_fix(&ctx->buffer)) { + /* OOM */ + return FAILURE; + } + + *decoded = NULL; + *decoded_len = 0; + + php_http_buffer_init(&tmp); + + /* we have data in our buffer */ + while (ctx->buffer.used) { + + /* we already know the size of the chunk and are waiting for data */ + if (ctx->hexlen) { + + /* not enough data buffered */ + if (ctx->buffer.used < ctx->hexlen) { + + /* flush anyway? */ + if (s->flags & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) { + /* flush all data (should only be chunk data) */ + php_http_buffer_append(&tmp, ctx->buffer.data, ctx->buffer.used); + /* waiting for less data now */ + ctx->hexlen -= ctx->buffer.used; + /* no more buffered data */ + php_http_buffer_reset(&ctx->buffer); + /* break */ + } + + /* we have too less data and don't need to flush */ + else { + break; + } + } + + /* we seem to have all data of the chunk */ + else { + php_http_buffer_append(&tmp, ctx->buffer.data, ctx->hexlen); + /* remove outgoing data from the buffer */ + php_http_buffer_cut(&ctx->buffer, 0, ctx->hexlen); + /* reset hexlen */ + ctx->hexlen = 0; + /* continue */ + } + } + + /* we don't know the length of the chunk yet */ + else { + size_t off = 0; + + /* ignore preceeding CRLFs (too loose?) */ + while (off < ctx->buffer.used && ( + ctx->buffer.data[off] == '\n' || + ctx->buffer.data[off] == '\r')) { + ++off; + } + if (off) { + php_http_buffer_cut(&ctx->buffer, 0, off); + } + + /* still data there? */ + if (ctx->buffer.used) { + int eollen; + const char *eolstr; + + /* we need eol, so we can be sure we have all hex digits */ + php_http_buffer_fix(&ctx->buffer); + if ((eolstr = php_http_locate_bin_eol(ctx->buffer.data, ctx->buffer.used, &eollen))) { + char *stop = NULL; + + /* read in chunk size */ + ctx->hexlen = strtoul(ctx->buffer.data, &stop, 16); + + /* if strtoul() stops at the beginning of the buffered data + there's something oddly wrong, i.e. bad input */ + if (stop == ctx->buffer.data) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse chunk len from '%.*s'", (int) MIN(16, ctx->buffer.used), ctx->buffer.data); ++ php_error_docref(NULL, E_WARNING, "Failed to parse chunk len from '%.*s'", (int) MIN(16, ctx->buffer.used), ctx->buffer.data); + php_http_buffer_dtor(&tmp); + return FAILURE; + } + + /* cut out */ + php_http_buffer_cut(&ctx->buffer, 0, eolstr + eollen - ctx->buffer.data); + /* buffer->hexlen is 0 now or contains the size of the next chunk */ + if (!ctx->hexlen) { + size_t off = 0; + + /* ignore following CRLFs (too loose?) */ + while (off < ctx->buffer.used && ( + ctx->buffer.data[off] == '\n' || + ctx->buffer.data[off] == '\r')) { + ++off; + } + if (off) { + php_http_buffer_cut(&ctx->buffer, 0, off); + } + + ctx->zeroed = 1; + break; + } + /* continue */ + } else { + /* we have not enough data buffered to read in chunk size */ + break; + } + } + /* break */ + } + } + + php_http_buffer_fix(&tmp); + *decoded = tmp.data; + *decoded_len = tmp.used; + + return SUCCESS; + } + + static ZEND_RESULT_CODE deflate_flush(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) + { + int status; + z_streamp ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc(*encoded_len); + + ctx->avail_in = 0; + ctx->next_in = NULL; + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + switch (status = deflate(ctx, Z_FULL_FLUSH)) { + case Z_OK: + case Z_STREAM_END: + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE - ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to flush deflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to flush deflate stream: %s", zError(status)); + return FAILURE; + } + + static ZEND_RESULT_CODE dechunk_flush(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len) + { + struct dechunk_ctx *ctx = s->ctx; + + if (ctx->hexlen) { + /* flush all data (should only be chunk data) */ + php_http_buffer_fix(&ctx->buffer); + php_http_buffer_data(&ctx->buffer, decoded, decoded_len); + /* waiting for less data now */ + ctx->hexlen -= ctx->buffer.used; + /* no more buffered data */ + php_http_buffer_reset(&ctx->buffer); + } else { + *decoded = NULL; + *decoded_len = 0; + } + + return SUCCESS; + } + + static ZEND_RESULT_CODE deflate_finish(php_http_encoding_stream_t *s, char **encoded, size_t *encoded_len) + { + int status; + z_streamp ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + *encoded_len = PHP_HTTP_DEFLATE_BUFFER_SIZE; + *encoded = emalloc(*encoded_len); + + /* deflate remaining input */ + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + ctx->avail_out = *encoded_len; + ctx->next_out = (Bytef *) *encoded; + + do { + status = deflate(ctx, Z_FINISH); + } while (Z_OK == status); + + if (Z_STREAM_END == status) { + /* cut processed input off */ + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + + /* size down */ + *encoded_len -= ctx->avail_out; + *encoded = erealloc(*encoded, *encoded_len + 1); + (*encoded)[*encoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*encoded, NULL); + *encoded_len = 0; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish deflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to finish deflate stream: %s", zError(status)); + return FAILURE; + } + + static ZEND_RESULT_CODE inflate_finish(php_http_encoding_stream_t *s, char **decoded, size_t *decoded_len) + { + int status; + z_streamp ctx = s->ctx; - TSRMLS_FETCH_FROM_CTX(s->ts); + + if (!PHP_HTTP_BUFFER(ctx->opaque)->used) { + *decoded = NULL; + *decoded_len = 0; + return SUCCESS; + } + + *decoded_len = (PHP_HTTP_BUFFER(ctx->opaque)->used + 1) * PHP_HTTP_INFLATE_ROUNDS; + *decoded = emalloc(*decoded_len); + + /* inflate remaining input */ + ctx->next_in = (Bytef *) PHP_HTTP_BUFFER(ctx->opaque)->data; + ctx->avail_in = PHP_HTTP_BUFFER(ctx->opaque)->used; + + ctx->avail_out = *decoded_len; + ctx->next_out = (Bytef *) *decoded; + + if (Z_STREAM_END == (status = inflate(ctx, Z_FINISH))) { + /* cut processed input off */ + php_http_buffer_cut(PHP_HTTP_BUFFER(ctx->opaque), 0, PHP_HTTP_BUFFER(ctx->opaque)->used - ctx->avail_in); + + /* size down */ + *decoded_len -= ctx->avail_out; + *decoded = erealloc(*decoded, *decoded_len + 1); + (*decoded)[*decoded_len] = '\0'; + return SUCCESS; + } + + PTR_SET(*decoded, NULL); + *decoded_len = 0; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish inflate stream: %s", zError(status)); ++ php_error_docref(NULL, E_WARNING, "Failed to finish inflate stream: %s", zError(status)); + return FAILURE; + } + + static zend_bool deflate_done(php_http_encoding_stream_t *s) + { + z_streamp ctx = s->ctx; + return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; + } + + static zend_bool inflate_done(php_http_encoding_stream_t *s) + { + z_streamp ctx = s->ctx; + return !ctx->avail_in && !PHP_HTTP_BUFFER(ctx->opaque)->used; + } + + static zend_bool dechunk_done(php_http_encoding_stream_t *s) + { + return ((struct dechunk_ctx *) s->ctx)->zeroed; + } + + static void deflate_dtor(php_http_encoding_stream_t *s) + { + if (s->ctx) { + z_streamp ctx = s->ctx; + + if (ctx->opaque) { + php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); + } + deflateEnd(ctx); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } + } + + static void inflate_dtor(php_http_encoding_stream_t *s) + { + if (s->ctx) { + z_streamp ctx = s->ctx; + + if (ctx->opaque) { + php_http_buffer_free((php_http_buffer_t **) &ctx->opaque); + } + inflateEnd(ctx); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } + } + + static void dechunk_dtor(php_http_encoding_stream_t *s) + { + if (s->ctx) { + struct dechunk_ctx *ctx = s->ctx; + + php_http_buffer_dtor(&ctx->buffer); + pefree(ctx, (s->flags & PHP_HTTP_ENCODING_STREAM_PERSISTENT)); + s->ctx = NULL; + } + } + + static php_http_encoding_stream_ops_t php_http_encoding_deflate_ops = { + deflate_init, + deflate_copy, + deflate_update, + deflate_flush, + deflate_done, + deflate_finish, + deflate_dtor + }; + + php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void) + { + return &php_http_encoding_deflate_ops; + } + + static php_http_encoding_stream_ops_t php_http_encoding_inflate_ops = { + inflate_init, + inflate_copy, + inflate_update, + NULL, + inflate_done, + inflate_finish, + inflate_dtor + }; + + php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void) + { + return &php_http_encoding_inflate_ops; + } + + static php_http_encoding_stream_ops_t php_http_encoding_dechunk_ops = { + dechunk_init, + dechunk_copy, + dechunk_update, + dechunk_flush, + dechunk_done, + NULL, + dechunk_dtor + }; + + php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void) + { + return &php_http_encoding_dechunk_ops; + } + + static zend_object_handlers php_http_encoding_stream_object_handlers; + -zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_encoding_stream_object_new(zend_class_entry *ce) + { - return php_http_encoding_stream_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_encoding_stream_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC) ++php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s) + { + php_http_encoding_stream_object_t *o; + - o = ecalloc(1, sizeof(*o)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - if (ptr) { - *ptr = o; - } ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + if (s) { + o->stream = s; + } + - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_encoding_stream_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_encoding_stream_object_handlers; ++ o->zo.handlers = &php_http_encoding_stream_object_handlers; + - return o->zv; ++ return o; + } + -zend_object_value php_http_encoding_stream_object_clone(zval *this_ptr TSRMLS_DC) ++zend_object *php_http_encoding_stream_object_clone(zval *object) + { - zend_object_value new_ov; - php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = PHP_HTTP_OBJ(NULL, object); ++ php_http_encoding_stream_t *cpy = php_http_encoding_stream_copy(old_obj->stream, NULL); + - new_ov = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, php_http_encoding_stream_copy(old_obj->stream, NULL), &new_obj TSRMLS_CC); - zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); ++ new_obj = php_http_encoding_stream_object_new_ex(old_obj->zo.ce, cpy); ++ zend_objects_clone_members(&new_obj->zo, &old_obj->zo); + - return new_ov; ++ return &new_obj->zo; + } + -void php_http_encoding_stream_object_free(void *object TSRMLS_DC) ++void php_http_encoding_stream_object_free(zend_object *object) + { - php_http_encoding_stream_object_t *o = (php_http_encoding_stream_object_t *) object; ++ php_http_encoding_stream_object_t *o = PHP_HTTP_OBJ(object, NULL); + + if (o->stream) { + php_http_encoding_stream_free(&o->stream); + } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); ++ zend_object_std_dtor(object); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream___construct, 0, 0, 0) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEncodingStream, __construct) + { - long flags = 0; ++ zend_long flags = 0; + php_http_encoding_stream_object_t *obj; + php_http_encoding_stream_ops_t *ops; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &flags), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->stream) { + php_http_throw(bad_method_call, "http\\Encoding\\Stream cannot be initialized twice", NULL); + return; + } + - if (instanceof_function(obj->zo.ce, php_http_deflate_stream_class_entry TSRMLS_CC)) { ++ if (instanceof_function(obj->zo.ce, php_http_deflate_stream_class_entry)) { + ops = &php_http_encoding_deflate_ops; - } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry TSRMLS_CC)) { ++ } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry)) { + ops = &php_http_encoding_inflate_ops; - } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry TSRMLS_CC)) { ++ } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry)) { + ops = &php_http_encoding_dechunk_ops; + } else { - php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name); ++ php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name->val); + return; + } + - php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags TSRMLS_CC), runtime, return); ++ php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags), runtime, return); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_update, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEncodingStream, update) + { - int data_len; ++ size_t data_len; + char *data_str; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len)) { - php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data_str, &data_len)) { ++ php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->stream) { ++ char *encoded_str = NULL; + size_t encoded_len; - char *encoded_str; + + if (SUCCESS == php_http_encoding_stream_update(obj->stream, data_str, data_len, &encoded_str, &encoded_len)) { - RETURN_STRINGL(encoded_str, encoded_len, 0); ++ if (encoded_str) { ++ RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); ++ } else { ++ RETURN_EMPTY_STRING(); ++ } + } + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_flush, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEncodingStream, flush) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->stream) { - char *encoded_str; ++ char *encoded_str = NULL; + size_t encoded_len; + + if (SUCCESS == php_http_encoding_stream_flush(obj->stream, &encoded_str, &encoded_len)) { + if (encoded_str) { - RETURN_STRINGL(encoded_str, encoded_len, 0); ++ RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_done, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEncodingStream, done) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->stream) { + RETURN_BOOL(php_http_encoding_stream_done(obj->stream)); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEncodingStream_finish, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEncodingStream, finish) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_encoding_stream_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_encoding_stream_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + if (obj->stream) { - char *encoded_str; ++ char *encoded_str = NULL; + size_t encoded_len; + + if (SUCCESS == php_http_encoding_stream_finish(obj->stream, &encoded_str, &encoded_len)) { + if (SUCCESS == php_http_encoding_stream_reset(&obj->stream)) { + if (encoded_str) { - RETURN_STRINGL(encoded_str, encoded_len, 0); ++ RETURN_STR(php_http_cs2zs(encoded_str, encoded_len)); + } else { + RETURN_EMPTY_STRING(); + } + } else { + PTR_FREE(encoded_str); + } + } + } + } + } + + static zend_function_entry php_http_encoding_stream_methods[] = { + PHP_ME(HttpEncodingStream, __construct, ai_HttpEncodingStream___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpEncodingStream, update, ai_HttpEncodingStream_update, ZEND_ACC_PUBLIC) + PHP_ME(HttpEncodingStream, flush, ai_HttpEncodingStream_flush, ZEND_ACC_PUBLIC) + PHP_ME(HttpEncodingStream, done, ai_HttpEncodingStream_done, ZEND_ACC_PUBLIC) + PHP_ME(HttpEncodingStream, finish, ai_HttpEncodingStream_finish, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpDeflateStream_encode, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpDeflateStream, encode) + { + char *str; - int len; - long flags = 0; ++ size_t len; ++ zend_long flags = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags)) { - char *enc_str; ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &len, &flags)) { ++ char *enc_str = NULL; + size_t enc_len; + - if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len TSRMLS_CC)) { - RETURN_STRINGL(enc_str, enc_len, 0); ++ if (SUCCESS == php_http_encoding_deflate(flags, str, len, &enc_str, &enc_len)) { ++ if (enc_str) { ++ RETURN_STR(php_http_cs2zs(enc_str, enc_len)); ++ } else { ++ RETURN_EMPTY_STRING(); ++ } + } + } + RETURN_FALSE; + } + + static zend_function_entry php_http_deflate_stream_methods[] = { + PHP_ME(HttpDeflateStream, encode, ai_HttpDeflateStream_encode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY + }; + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpInflateStream_decode, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpInflateStream, decode) + { + char *str; - int len; ++ size_t len; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { - char *enc_str; ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len)) { ++ char *enc_str = NULL; + size_t enc_len; + - if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len TSRMLS_CC)) { - RETURN_STRINGL(enc_str, enc_len, 0); ++ if (SUCCESS == php_http_encoding_inflate(str, len, &enc_str, &enc_len)) { ++ if (enc_str) { ++ RETURN_STR(php_http_cs2zs(enc_str, enc_len)); ++ } else { ++ RETURN_EMPTY_STRING(); ++ } + } + } + RETURN_FALSE; + } + + static zend_function_entry php_http_inflate_stream_methods[] = { + PHP_ME(HttpInflateStream, decode, ai_HttpInflateStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY + }; + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpDechunkStream_decode, 0, 0, 1) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(1, decoded_len) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpDechunkStream, decode) + { + char *str; - int len; ++ size_t len; + zval *zlen = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &str, &len, &zlen)) { + const char *end_ptr; - char *enc_str; ++ char *enc_str = NULL; + size_t enc_len; + - if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len TSRMLS_CC))) { ++ if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len))) { + if (zlen) { ++ ZVAL_DEREF(zlen); + zval_dtor(zlen); + ZVAL_LONG(zlen, str + len - end_ptr); + } - RETURN_STRINGL(enc_str, enc_len, 0); ++ if (enc_str) { ++ RETURN_STR(php_http_cs2zs(enc_str, enc_len)); ++ } else { ++ RETURN_EMPTY_STRING(); ++ } + } + } + RETURN_FALSE; + } + + static zend_function_entry php_http_dechunk_stream_methods[] = { + PHP_ME(HttpDechunkStream, decode, ai_HttpDechunkStream_decode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_encoding_stream_class_entry; + zend_class_entry *php_http_deflate_stream_class_entry; + zend_class_entry *php_http_inflate_stream_class_entry; + zend_class_entry *php_http_dechunk_stream_class_entry; + + PHP_MINIT_FUNCTION(http_encoding) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding", "Stream", php_http_encoding_stream_methods); - php_http_encoding_stream_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_encoding_stream_class_entry = zend_register_internal_class(&ce); + php_http_encoding_stream_class_entry->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; + php_http_encoding_stream_class_entry->create_object = php_http_encoding_stream_object_new; + memcpy(&php_http_encoding_stream_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ++ php_http_encoding_stream_object_handlers.offset = XtOffsetOf(php_http_encoding_stream_object_t, zo); + php_http_encoding_stream_object_handlers.clone_obj = php_http_encoding_stream_object_clone; ++ php_http_encoding_stream_object_handlers.free_obj = php_http_encoding_stream_object_free; + - zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_NONE"), PHP_HTTP_ENCODING_STREAM_FLUSH_NONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC TSRMLS_CC); - zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_NONE"), PHP_HTTP_ENCODING_STREAM_FLUSH_NONE); ++ zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_SYNC"), PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC); ++ zend_declare_class_constant_long(php_http_encoding_stream_class_entry, ZEND_STRL("FLUSH_FULL"), PHP_HTTP_ENCODING_STREAM_FLUSH_FULL); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Deflate", php_http_deflate_stream_methods); - php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry, NULL TSRMLS_CC); - - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE TSRMLS_CC); - zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED TSRMLS_CC); ++ php_http_deflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); ++ php_http_deflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; ++ ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_GZIP"), PHP_HTTP_DEFLATE_TYPE_GZIP); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_ZLIB"), PHP_HTTP_DEFLATE_TYPE_ZLIB); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("TYPE_RAW"), PHP_HTTP_DEFLATE_TYPE_RAW); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_DEF"), PHP_HTTP_DEFLATE_LEVEL_DEF); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MIN"), PHP_HTTP_DEFLATE_LEVEL_MIN); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("LEVEL_MAX"), PHP_HTTP_DEFLATE_LEVEL_MAX); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_DEF"), PHP_HTTP_DEFLATE_STRATEGY_DEF); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FILT"), PHP_HTTP_DEFLATE_STRATEGY_FILT); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_HUFF"), PHP_HTTP_DEFLATE_STRATEGY_HUFF); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_RLE"), PHP_HTTP_DEFLATE_STRATEGY_RLE); ++ zend_declare_class_constant_long(php_http_deflate_stream_class_entry, ZEND_STRL("STRATEGY_FIXED"), PHP_HTTP_DEFLATE_STRATEGY_FIXED); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Inflate", php_http_inflate_stream_methods); - php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry, NULL TSRMLS_CC); ++ php_http_inflate_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); ++ php_http_inflate_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Encoding\\Stream", "Dechunk", php_http_dechunk_stream_methods); - php_http_dechunk_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry, NULL TSRMLS_CC); ++ php_http_dechunk_stream_class_entry = zend_register_internal_class_ex(&ce, php_http_encoding_stream_class_entry); ++ php_http_dechunk_stream_class_entry->create_object = php_http_encoding_stream_object_new; + + return SUCCESS; + } + + + /* + * 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 --cc src/php_http_encoding.h index 0000000,cdf7a1a..ea767dd mode 000000,100644..100644 --- a/src/php_http_encoding.h +++ b/src/php_http_encoding.h @@@ -1,0 -1,197 +1,193 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_ENCODING_H + #define PHP_HTTP_ENCODING_H + + #include + + extern PHP_MINIT_FUNCTION(http_encoding); + extern PHP_RINIT_FUNCTION(http_encoding); + extern PHP_RSHUTDOWN_FUNCTION(http_encoding); + + #define PHP_HTTP_INFLATE_ROUNDS 100 + + #define PHP_HTTP_DEFLATE_BUFFER_SIZE_GUESS(S) \ + (((size_t) ((double) S * (double) 1.015)) + 10 + 8 + 4 + 1) + #define PHP_HTTP_INFLATE_BUFFER_SIZE_GUESS(S) \ + (((S) + 1) << 3) + #define PHP_HTTP_INFLATE_BUFFER_SIZE_ALIGN(S) \ + ((S) += (S) >> (3)) + + #define PHP_HTTP_DEFLATE_BUFFER_SIZE 0x8000 + #define PHP_HTTP_INFLATE_BUFFER_SIZE 0x1000 + + #define PHP_HTTP_DEFLATE_LEVEL_DEF 0x00000000 + #define PHP_HTTP_DEFLATE_LEVEL_MIN 0x00000001 + #define PHP_HTTP_DEFLATE_LEVEL_MAX 0x00000009 + #define PHP_HTTP_DEFLATE_TYPE_ZLIB 0x00000000 + #define PHP_HTTP_DEFLATE_TYPE_GZIP 0x00000010 + #define PHP_HTTP_DEFLATE_TYPE_RAW 0x00000020 + #define PHP_HTTP_DEFLATE_STRATEGY_DEF 0x00000000 + #define PHP_HTTP_DEFLATE_STRATEGY_FILT 0x00000100 + #define PHP_HTTP_DEFLATE_STRATEGY_HUFF 0x00000200 + #define PHP_HTTP_DEFLATE_STRATEGY_RLE 0x00000300 + #define PHP_HTTP_DEFLATE_STRATEGY_FIXED 0x00000400 + + #define PHP_HTTP_DEFLATE_LEVEL_SET(flags, level) \ + switch (flags & 0xf) \ + { \ + default: \ + if ((flags & 0xf) < 10) { \ + level = flags & 0xf; \ + break; \ + } \ + case PHP_HTTP_DEFLATE_LEVEL_DEF: \ + level = Z_DEFAULT_COMPRESSION; \ + break; \ + } + + #define PHP_HTTP_DEFLATE_WBITS_SET(flags, wbits) \ + switch (flags & 0xf0) \ + { \ + case PHP_HTTP_DEFLATE_TYPE_GZIP: \ + wbits = PHP_HTTP_WINDOW_BITS_GZIP; \ + break; \ + case PHP_HTTP_DEFLATE_TYPE_RAW: \ + wbits = PHP_HTTP_WINDOW_BITS_RAW; \ + break; \ + default: \ + wbits = PHP_HTTP_WINDOW_BITS_ZLIB; \ + break; \ + } + + #define PHP_HTTP_INFLATE_WBITS_SET(flags, wbits) \ + if (flags & PHP_HTTP_INFLATE_TYPE_RAW) { \ + wbits = PHP_HTTP_WINDOW_BITS_RAW; \ + } else { \ + wbits = PHP_HTTP_WINDOW_BITS_ANY; \ + } + + #define PHP_HTTP_DEFLATE_STRATEGY_SET(flags, strategy) \ + switch (flags & 0xf00) \ + { \ + case PHP_HTTP_DEFLATE_STRATEGY_FILT: \ + strategy = Z_FILTERED; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_HUFF: \ + strategy = Z_HUFFMAN_ONLY; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_RLE: \ + strategy = Z_RLE; \ + break; \ + case PHP_HTTP_DEFLATE_STRATEGY_FIXED: \ + strategy = Z_FIXED; \ + break; \ + default: \ + strategy = Z_DEFAULT_STRATEGY; \ + break; \ + } + + #define PHP_HTTP_WINDOW_BITS_ZLIB 0x0000000f + #define PHP_HTTP_WINDOW_BITS_GZIP 0x0000001f + #define PHP_HTTP_WINDOW_BITS_ANY 0x0000002f + #define PHP_HTTP_WINDOW_BITS_RAW -0x000000f + + #ifndef Z_FIXED + /* Z_FIXED does not exist prior 1.2.2.2 */ + # define Z_FIXED 0 + #endif + + #define PHP_HTTP_INFLATE_TYPE_ZLIB 0x00000000 + #define PHP_HTTP_INFLATE_TYPE_GZIP 0x00000000 + #define PHP_HTTP_INFLATE_TYPE_RAW 0x00000001 + + #define PHP_HTTP_ENCODING_STREAM_FLUSH_NONE 0x00000000 + #define PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC 0x00100000 + #define PHP_HTTP_ENCODING_STREAM_FLUSH_FULL 0x00200000 + + #define PHP_HTTP_ENCODING_STREAM_FLUSH_FLAG(f) \ + (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_FULL) ? Z_FULL_FLUSH : \ + (((f) & PHP_HTTP_ENCODING_STREAM_FLUSH_SYNC) ? Z_SYNC_FLUSH : Z_NO_FLUSH)) + + #define PHP_HTTP_ENCODING_STREAM_PERSISTENT 0x01000000 + + typedef struct php_http_encoding_stream php_http_encoding_stream_t; + + typedef php_http_encoding_stream_t *(*php_http_encoding_stream_init_func_t)(php_http_encoding_stream_t *s); + typedef php_http_encoding_stream_t *(*php_http_encoding_stream_copy_func_t)(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to); + typedef ZEND_RESULT_CODE (*php_http_encoding_stream_update_func_t)(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len); + typedef ZEND_RESULT_CODE (*php_http_encoding_stream_flush_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len); + typedef zend_bool (*php_http_encoding_stream_done_func_t)(php_http_encoding_stream_t *s); + typedef ZEND_RESULT_CODE (*php_http_encoding_stream_finish_func_t)(php_http_encoding_stream_t *s, char **out_str, size_t *out_len); + typedef void (*php_http_encoding_stream_dtor_func_t)(php_http_encoding_stream_t *s); + + typedef struct php_http_encoding_stream_ops { + php_http_encoding_stream_init_func_t init; + php_http_encoding_stream_copy_func_t copy; + php_http_encoding_stream_update_func_t update; + php_http_encoding_stream_flush_func_t flush; + php_http_encoding_stream_done_func_t done; + php_http_encoding_stream_finish_func_t finish; + php_http_encoding_stream_dtor_func_t dtor; + } php_http_encoding_stream_ops_t; + + struct php_http_encoding_stream { + unsigned flags; + void *ctx; + php_http_encoding_stream_ops_t *ops; -#ifdef ZTS - void ***ts; -#endif + }; + + PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_deflate_ops(void); + PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_inflate_ops(void); + PHP_HTTP_API php_http_encoding_stream_ops_t *php_http_encoding_stream_get_dechunk_ops(void); + -PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags TSRMLS_DC); ++PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_init(php_http_encoding_stream_t *s, php_http_encoding_stream_ops_t *ops, unsigned flags); + PHP_HTTP_API php_http_encoding_stream_t *php_http_encoding_stream_copy(php_http_encoding_stream_t *from, php_http_encoding_stream_t *to); + PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_reset(php_http_encoding_stream_t **s); + PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_update(php_http_encoding_stream_t *s, const char *in_str, size_t in_len, char **out_str, size_t *out_len); + PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_flush(php_http_encoding_stream_t *s, char **out_str, size_t *len); + PHP_HTTP_API zend_bool php_http_encoding_stream_done(php_http_encoding_stream_t *s); + PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_stream_finish(php_http_encoding_stream_t *s, char **out_str, size_t *len); + PHP_HTTP_API void php_http_encoding_stream_dtor(php_http_encoding_stream_t *s); + PHP_HTTP_API void php_http_encoding_stream_free(php_http_encoding_stream_t **s); + -PHP_HTTP_API const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC); ++PHP_HTTP_API const char *php_http_encoding_dechunk(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_deflate(int flags, const char *data, size_t data_len, char **encoded, size_t *encoded_len); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len); + + typedef struct php_http_encoding_stream_object { - zend_object zo; - zend_object_value zv; + php_http_encoding_stream_t *stream; ++ zend_object zo; + } php_http_encoding_stream_object_t; + + PHP_HTTP_API zend_class_entry *php_http_encoding_stream_class_entry; + -zend_object_value php_http_encoding_stream_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s, php_http_encoding_stream_object_t **ptr TSRMLS_DC); -zend_object_value php_http_encoding_stream_object_clone(zval *object TSRMLS_DC); -void php_http_encoding_stream_object_free(void *object TSRMLS_DC); ++zend_object *php_http_encoding_stream_object_new(zend_class_entry *ce); ++php_http_encoding_stream_object_t *php_http_encoding_stream_object_new_ex(zend_class_entry *ce, php_http_encoding_stream_t *s); ++zend_object *php_http_encoding_stream_object_clone(zval *object); ++void php_http_encoding_stream_object_free(zend_object *object); + + PHP_HTTP_API zend_class_entry *php_http_deflate_stream_class_entry; + PHP_HTTP_API zend_class_entry *php_http_inflate_stream_class_entry; + PHP_HTTP_API zend_class_entry *php_http_dechunk_stream_class_entry; + + #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 --cc src/php_http_env.c index 0000000,c60d4c0..df22d5a mode 000000,100644..100644 --- a/src/php_http_env.c +++ b/src/php_http_env.c @@@ -1,0 -1,819 +1,818 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + #include "php_variables.h" + ++ + PHP_RSHUTDOWN_FUNCTION(http_env) + { + if (PHP_HTTP_G->env.request.headers) { + zend_hash_destroy(PHP_HTTP_G->env.request.headers); + FREE_HASHTABLE(PHP_HTTP_G->env.request.headers); + PHP_HTTP_G->env.request.headers = NULL; + } + if (PHP_HTTP_G->env.request.body) { + php_http_message_body_free(&PHP_HTTP_G->env.request.body); + } + + if (PHP_HTTP_G->env.server_var) { - zval_ptr_dtor(&PHP_HTTP_G->env.server_var); ++ zval_ptr_dtor(PHP_HTTP_G->env.server_var); + PHP_HTTP_G->env.server_var = NULL; + } + + return SUCCESS; + } + -void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC) ++void php_http_env_get_request_headers(HashTable *headers) + { - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **hsv, **header; - HashPosition pos; ++ php_http_arrkey_t key; ++ zval *hsv, *header; + + if (!PHP_HTTP_G->env.request.headers) { + ALLOC_HASHTABLE(PHP_HTTP_G->env.request.headers); - zend_hash_init(PHP_HTTP_G->env.request.headers, 0, NULL, ZVAL_PTR_DTOR, 0); - - zend_is_auto_global("_SERVER", lenof("_SERVER") TSRMLS_CC); ++ ZEND_INIT_SYMTABLE(PHP_HTTP_G->env.request.headers); + - if (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void *) &hsv) && Z_TYPE_PP(hsv) == IS_ARRAY) { - FOREACH_KEY(pos, *hsv, key) { - if (key.type == HASH_KEY_IS_STRING && key.len > 6 && *key.str == 'H' && !strncmp(key.str, "HTTP_", 5)) { - key.len -= 5; - key.str = php_http_pretty_key(estrndup(key.str + 5, key.len - 1), key.len - 1, 1, 1); ++ if ((hsv = php_http_env_get_superglobal(ZEND_STRL("_SERVER")))) { ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(hsv), key.h, key.key, header) ++ { ++ if (key.key && key.key->len > 5 && *key.key->val == 'H' && !strncmp(key.key->val, "HTTP_", 5)) { ++ size_t key_len = key.key->len - 5; ++ char *key_str = php_http_pretty_key(estrndup(&key.key->val[5], key_len), key_len, 1, 1); + - zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos); - Z_ADDREF_P(*header); - zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL); ++ Z_TRY_ADDREF_P(header); ++ zend_symtable_str_update(PHP_HTTP_G->env.request.headers, key_str, key_len, header); + - efree(key.str); - } else if (key.type == HASH_KEY_IS_STRING && key.len > 9 && *key.str == 'C' && !strncmp(key.str, "CONTENT_", 8)) { - key.str = php_http_pretty_key(estrndup(key.str, key.len - 1), key.len - 1, 1, 1); ++ efree(key_str); ++ } else if (key.key && key.key->len > 8 && *key.key->val == 'C' && !strncmp(key.key->val, "CONTENT_", 8)) { ++ char *key_str = php_http_pretty_key(estrndup(key.key->val, key.key->len), key.key->len, 1, 1); + - zend_hash_get_current_data_ex(Z_ARRVAL_PP(hsv), (void *) &header, &pos); - Z_ADDREF_P(*header); - zend_symtable_update(PHP_HTTP_G->env.request.headers, key.str, key.len, (void *) header, sizeof(zval *), NULL); ++ Z_TRY_ADDREF_P(header); ++ zend_symtable_str_update(PHP_HTTP_G->env.request.headers, key_str, key.key->len, header); + - efree(key.str); ++ efree(key_str); + } + } ++ ZEND_HASH_FOREACH_END(); + } + } + + if (headers) { - zend_hash_copy(headers, PHP_HTTP_G->env.request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ array_copy(PHP_HTTP_G->env.request.headers, headers); + } + } + -char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC) ++char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request) + { + HashTable *request_headers; - zval **zvalue = NULL; ++ zval *zvalue = NULL; + char *val = NULL, *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); + + if (request) { + request_headers = &request->hdrs; + } else { - php_http_env_get_request_headers(NULL TSRMLS_CC); ++ php_http_env_get_request_headers(NULL); + request_headers = PHP_HTTP_G->env.request.headers; + } + - if (SUCCESS == zend_symtable_find(request_headers, key, name_len + 1, (void *) &zvalue)) { - zval *zcopy = php_http_ztyp(IS_STRING, *zvalue); ++ if ((zvalue = zend_symtable_str_find(request_headers, key, name_len))) { ++ zend_string *zs = zval_get_string(zvalue); + - val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); ++ val = estrndup(zs->val, zs->len); + if (len) { - *len = Z_STRLEN_P(zcopy); ++ *len = zs->len; + } - zval_ptr_dtor(&zcopy); ++ zend_string_release(zs); + } + + efree(key); + + return val; + } + -int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC) ++zend_bool php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request) + { + HashTable *request_headers; + char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); - int got; ++ zend_bool got; + + if (request) { + request_headers = &request->hdrs; + } else { - php_http_env_get_request_headers(NULL TSRMLS_CC); ++ php_http_env_get_request_headers(NULL); + request_headers = PHP_HTTP_G->env.request.headers; + } - got = zend_symtable_exists(request_headers, key, name_len + 1); ++ got = zend_symtable_str_exists(request_headers, key, name_len); + efree(key); + + return got; + } + -zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC) ++zval *php_http_env_get_superglobal(const char *key, size_t key_len) + { - zval **hsv; ++ zval *hsv; ++ zend_string *key_str = zend_string_init(key, key_len, 0); + - zend_is_auto_global(key, key_len TSRMLS_CC); ++ zend_is_auto_global(key_str); ++ hsv = zend_hash_find(&EG(symbol_table), key_str); ++ zend_string_release(key_str); + - if ((SUCCESS != zend_hash_find(&EG(symbol_table), key, key_len + 1, (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) { ++ if (Z_TYPE_P(hsv) != IS_ARRAY) { + return NULL; + } + - return *hsv; ++ return hsv; + } + -zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check TSRMLS_DC) ++zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check) + { - zval *hsv, **var; - char *env; ++ zval *hsv, *var; + - /* if available, this is a lot faster than accessing $_SERVER */ ++ /* if available, this is a lot faster than accessing $_SERVER * / + if (sapi_module.getenv) { - if ((!(env = sapi_module.getenv((char *) key, key_len TSRMLS_CC))) || (check && !*env)) { ++ char *env; ++ ++ if ((!(env = sapi_module.getenv((char *) key, key_len))) || (check && !*env)) { + return NULL; + } + if (PHP_HTTP_G->env.server_var) { + zval_ptr_dtor(&PHP_HTTP_G->env.server_var); + } + MAKE_STD_ZVAL(PHP_HTTP_G->env.server_var); + ZVAL_STRING(PHP_HTTP_G->env.server_var, env, 1); + return PHP_HTTP_G->env.server_var; + } ++ / * */ + - if (!(hsv = php_http_env_get_superglobal(ZEND_STRL("_SERVER") TSRMLS_CC))) { ++ if (!(hsv = php_http_env_get_superglobal(ZEND_STRL("_SERVER")))) { + return NULL; + } - if ((SUCCESS != zend_symtable_find(Z_ARRVAL_P(hsv), key, key_len + 1, (void *) &var))) { ++ if (!(var = zend_symtable_str_find(Z_ARRVAL_P(hsv), key, key_len))) { + return NULL; + } - if (check && !((Z_TYPE_PP(var) == IS_STRING) && Z_STRVAL_PP(var) && Z_STRLEN_PP(var))) { ++ if (check && !((Z_TYPE_P(var) == IS_STRING) && Z_STRVAL_P(var) && Z_STRLEN_P(var))) { + return NULL; + } - return *var; ++ return var; + } + -php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D) ++php_http_message_body_t *php_http_env_get_request_body(void) + { + if (!PHP_HTTP_G->env.request.body) { + php_stream *s = php_stream_temp_new(); -#if PHP_VERSION_ID >= 50600 + php_stream *input = php_stream_open_wrapper("php://input", "r", 0, NULL); + + /* php://input does not support stat */ + php_stream_copy_to_stream_ex(input, s, -1, NULL); + php_stream_close(input); -#else - if (SG(request_info).post_data || SG(request_info).raw_post_data) { - /* php://input does not support seek() in PHP <= 5.5 */ - if (SG(request_info).raw_post_data) { - php_stream_write(s, SG(request_info).raw_post_data, SG(request_info).raw_post_data_length); - } else { - php_stream_write(s, SG(request_info).post_data, SG(request_info).post_data_length); - } - } else if (sapi_module.read_post && !SG(read_post_bytes)) { - char *buf = emalloc(4096); - int len; - - while (0 < (len = sapi_module.read_post(buf, 4096 TSRMLS_CC))) { - SG(read_post_bytes) += len; - php_stream_write(s, buf, len); + - if (len < 4096) { - break; - } - } - efree(buf); - } -#endif + php_stream_rewind(s); - PHP_HTTP_G->env.request.body = php_http_message_body_init(NULL, s TSRMLS_CC); ++ PHP_HTTP_G->env.request.body = php_http_message_body_init(NULL, s); + } + + return PHP_HTTP_G->env.request.body; + } + -const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC) ++const char *php_http_env_get_request_method(php_http_message_t *request) + { + const char *m; + + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, request)) { + m = request->http.info.request.method; + } else { + m = SG(request_info).request_method; + } + + return m ? m : "GET"; + } + -php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length, php_http_message_t *request TSRMLS_DC) ++php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t length, php_http_message_t *request) + { - zval *zentry; ++ zval zentry; + char *range, *rp, c; + long begin = -1, end = -1, *ptr; + - if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL, request TSRMLS_CC))) { ++ if (!(range = php_http_env_get_request_header(ZEND_STRL("Range"), NULL, request))) { + return PHP_HTTP_RANGE_NO; + } + if (strncmp(range, "bytes=", lenof("bytes="))) { + PTR_FREE(range); + return PHP_HTTP_RANGE_NO; + } + + rp = range + lenof("bytes="); + ptr = &begin; + + do { + switch (c = *(rp++)) { + case '0': + /* allow 000... - shall we? */ + if (*ptr != -10) { + *ptr *= 10; + } + break; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + /* + * If the value of the pointer is already set (non-negative) + * then multiply its value by ten and add the current value, + * else initialise the pointers value with the current value + * -- + * This let us recognize empty fields when validating the + * ranges, i.e. a "-10" for begin and "12345" for the end + * was the following range request: "Range: bytes=0-12345"; + * While a "-1" for begin and "12345" for the end would + * have been: "Range: bytes=-12345". + */ + if (*ptr > 0) { + *ptr *= 10; + *ptr += c - '0'; + } else { + *ptr = c - '0'; + } + break; + + case '-': + ptr = &end; + break; + + case ' ': + break; + + case 0: + case ',': + + if (length) { + /* validate ranges */ + switch (begin) { + /* "0-12345" */ + case -10: + switch (end) { + /* "0-" */ + case -1: + PTR_FREE(range); + return PHP_HTTP_RANGE_NO; + + /* "0-0" */ + case -10: + end = 0; + break; + + default: + if (length <= (size_t) end) { + end = length - 1; + } + break; + } + begin = 0; + break; + + /* "-12345" */ + case -1: + /* "-", "-0" */ + if (end == -1 || end == -10) { + PTR_FREE(range); + return PHP_HTTP_RANGE_ERR; + } + begin = length - end; + end = length - 1; + break; + + /* "12345-(NNN)" */ + default: + if (length <= (size_t) begin) { + PTR_FREE(range); + return PHP_HTTP_RANGE_ERR; + } + switch (end) { + /* "12345-0" */ + case -10: + PTR_FREE(range); + return PHP_HTTP_RANGE_ERR; + + /* "12345-" */ + case -1: + end = length - 1; + break; + + /* "12345-67890" */ + default: + if (length <= (size_t) end) { + end = length - 1; + } else if (end < begin) { + PTR_FREE(range); + return PHP_HTTP_RANGE_ERR; + } + break; + } + break; + } + } + - MAKE_STD_ZVAL(zentry); - array_init(zentry); - add_index_long(zentry, 0, begin); - add_index_long(zentry, 1, end); - zend_hash_next_index_insert(ranges, &zentry, sizeof(zval *), NULL); ++ array_init(&zentry); ++ add_index_long(&zentry, 0, begin); ++ add_index_long(&zentry, 1, end); ++ zend_hash_next_index_insert(ranges, &zentry); + + begin = -1; + end = -1; + ptr = &begin; + + break; + + default: + PTR_FREE(range); + return PHP_HTTP_RANGE_NO; + } + } while (c != 0); + + PTR_FREE(range); + return PHP_HTTP_RANGE_OK; + } + -static void grab_headers(void *data, void *arg TSRMLS_DC) ++static void grab_headers(void *data, void *arg) + { + php_http_buffer_appendl(PHP_HTTP_BUFFER(arg), ((sapi_header_struct *)data)->header); + php_http_buffer_appends(PHP_HTTP_BUFFER(arg), PHP_HTTP_CRLF); + } + -ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC) ++static void grab_header(void *data, void *arg) ++{ ++ struct { ++ char *name_str; ++ size_t name_len; ++ char *value_ptr; ++ } *args = arg; ++ sapi_header_struct *header = data; ++ ++ if ( header->header_len > args->name_len ++ && header->header[args->name_len] == ':' ++ && !strncmp(header->header, args->name_str, args->name_len) ++ ) { ++ args->value_ptr = &header->header[args->name_len + 1]; ++ while (PHP_HTTP_IS_CTYPE(space, *args->value_ptr)) { ++ ++args->value_ptr; ++ } ++ } ++} ++ ++ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht) + { + ZEND_RESULT_CODE status; + php_http_buffer_t headers; + + php_http_buffer_init(&headers); - zend_llist_apply_with_argument(&SG(sapi_headers).headers, grab_headers, &headers TSRMLS_CC); ++ zend_llist_apply_with_argument(&SG(sapi_headers).headers, grab_headers, &headers); + php_http_buffer_fix(&headers); + - status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL TSRMLS_CC); ++ status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL); + php_http_buffer_dtor(&headers); + + return status; + } + -char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC) ++char *php_http_env_get_response_header(const char *name_str, size_t name_len) + { - char *val = NULL; - HashTable headers; - - zend_hash_init(&headers, 0, NULL, ZVAL_PTR_DTOR, 0); - if (SUCCESS == php_http_env_get_response_headers(&headers TSRMLS_CC)) { - zval **zvalue; - char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); ++ struct { ++ char *name_str; ++ size_t name_len; ++ char *value_ptr; ++ } args; + - if (SUCCESS == zend_symtable_find(&headers, key, name_len + 1, (void *) &zvalue)) { - zval *zcopy = php_http_ztyp(IS_STRING, *zvalue); ++ args.name_str = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); ++ args.name_len = name_len; ++ args.value_ptr = NULL; ++ zend_llist_apply_with_argument(&SG(sapi_headers).headers, grab_header, &args); ++ efree(args.name_str); + - val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); - zval_ptr_dtor(&zcopy); - } - - efree(key); - } - zend_hash_destroy(&headers); - - return val; ++ return args.value_ptr ? estrdup(args.value_ptr) : NULL; + } + -long php_http_env_get_response_code(TSRMLS_D) ++long php_http_env_get_response_code(void) + { + long code = SG(sapi_headers).http_response_code; + return code ? code : 200; + } + -ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_code(long http_code) + { - return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) http_code TSRMLS_CC); ++ return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) (zend_intptr_t) http_code); + } + -ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v) + { + sapi_header_line h = {NULL, 0, 0}; + ZEND_RESULT_CODE ret; + + h.line_len = spprintf(&h.line, 0, "HTTP/%u.%u %ld %s", v->major, v->minor, code, php_http_env_get_response_status_for_code(code)); - ret = sapi_header_op(SAPI_HEADER_REPLACE, (void *) &h TSRMLS_CC); ++ ret = sapi_header_op(SAPI_HEADER_REPLACE, (void *) &h); + efree(h.line); + + return ret; + } + -ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v) + { - return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C), v TSRMLS_CC); ++ return php_http_env_set_response_status_line(php_http_env_get_response_code(), v); + } + -ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace) + { + sapi_header_line h = {estrndup(header_str, header_len), header_len, http_code}; - ZEND_RESULT_CODE ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC); ++ ZEND_RESULT_CODE ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h); ++ + efree(h.line); + return ret; + } + -ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv) + { + ZEND_RESULT_CODE ret = FAILURE; + sapi_header_line h = {NULL, 0, http_code}; + + h.line_len = vspprintf(&h.line, 0, fmt, argv); + + if (h.line) { + if (h.line_len) { - ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC); ++ ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h); + } + efree(h.line); + } + return ret; + } + -ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...) ++ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace, const char *fmt, ...) + { + ZEND_RESULT_CODE ret; + va_list args; + + va_start(args, fmt); - ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC); ++ ret = php_http_env_set_response_header_va(http_code, replace, fmt, args); + va_end(args); + + return ret; + } + -ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC) ++ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace) + { + if (!value) { + sapi_header_line h = {(char *) name_str, name_len, http_code}; + - return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h TSRMLS_CC); ++ return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h); + } + - if(Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) { - HashPosition pos; ++ if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) { + int first = replace; - zval **data_ptr; ++ zval *data_ptr; ++ HashTable *ht = HASH_OF(value); + - FOREACH_HASH_VAL(pos, HASH_OF(value), data_ptr) { - if (SUCCESS != php_http_env_set_response_header_value(http_code, name_str, name_len, *data_ptr, first TSRMLS_CC)) { ++ ZEND_HASH_FOREACH_VAL_IND(ht, data_ptr) ++ { ++ if (SUCCESS != php_http_env_set_response_header_value(http_code, name_str, name_len, data_ptr, first)) { + return FAILURE; + } + first = 0; + } ++ ZEND_HASH_FOREACH_END(); + + return SUCCESS; + } else { - zval *data = php_http_ztyp(IS_STRING, value); ++ zend_string *data = zval_get_string(value); + - if (!Z_STRLEN_P(data)) { - zval_ptr_dtor(&data); - return php_http_env_set_response_header_value(http_code, name_str, name_len, NULL, replace TSRMLS_CC); ++ if (!data->len) { ++ zend_string_release(data); ++ return php_http_env_set_response_header_value(http_code, name_str, name_len, NULL, replace); + } else { + sapi_header_line h; + ZEND_RESULT_CODE ret; + + if (name_len > INT_MAX) { - name_len = INT_MAX; ++ return FAILURE; + } + h.response_code = http_code; - h.line_len = spprintf(&h.line, 0, "%.*s: %.*s", (int) name_len, name_str, Z_STRLEN_P(data), Z_STRVAL_P(data)); ++ h.line_len = spprintf(&h.line, 0, "%.*s: %.*s", (int) name_len, name_str, data->len, data->val); + - ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC); ++ ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h); + - zval_ptr_dtor(&data); ++ zend_string_release(data); + PTR_FREE(h.line); + + return ret; + } + } + } + + const char *php_http_env_get_response_status_for_code(unsigned code) + { + switch (code) { + #define PHP_HTTP_RESPONSE_CODE(c, s) case c: return s; + #include "php_http_response_codes.h" + #undef PHP_HTTP_RESPONSE_CODE + default: + return NULL; + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestHeader, 0, 0, 0) + ZEND_ARG_INFO(0, header_name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getRequestHeader) + { + char *header_name_str = NULL; - int header_name_len = 0; ++ size_t header_name_len = 0; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &header_name_str, &header_name_len)) { + return; + } + if (header_name_str && header_name_len) { + size_t header_length; - char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length, NULL TSRMLS_CC); ++ char *header_value = php_http_env_get_request_header(header_name_str, header_name_len, &header_length, NULL); + + if (header_value) { - RETURN_STRINGL(header_value, header_length, 0); ++ RETURN_STR(php_http_cs2zs(header_value, header_length)); + } + } else { + array_init(return_value); - php_http_env_get_request_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); ++ php_http_env_get_request_headers(Z_ARRVAL_P(return_value)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getRequestBody, 0, 0, 0) + ZEND_ARG_INFO(0, body_class_name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getRequestBody) + { - zend_object_value ov; + php_http_message_body_t *body; ++ php_http_message_body_object_t *body_obj; + zend_class_entry *class_entry = php_http_message_body_class_entry; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|C", &class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|C", &class_entry), invalid_arg, return); + - body = php_http_env_get_request_body(TSRMLS_C); - if (SUCCESS == php_http_new(&ov, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, body, NULL TSRMLS_CC)) { ++ body = php_http_env_get_request_body(); ++ if (SUCCESS == php_http_new((void *) &body_obj, class_entry, (php_http_new_t) php_http_message_body_object_new_ex, php_http_message_body_class_entry, body)) { + php_http_message_body_addref(body); - RETVAL_OBJVAL(ov, 0); ++ RETVAL_OBJ(&body_obj->zo); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForCode, 0, 0, 1) + ZEND_ARG_INFO(0, code) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getResponseStatusForCode) + { - long code; ++ zend_long code; + const char *status; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code)) { + return; + } + + if ((status = php_http_env_get_response_status_for_code(code))) { - RETURN_STRING(status, 1); ++ RETURN_STRING(status); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseStatusForAllCodes, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getResponseStatusForAllCodes) + { + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + + array_init(return_value); -#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status, 1); ++#define PHP_HTTP_RESPONSE_CODE(code, status) add_index_string(return_value, code, status); + #include "php_http_response_codes.h" + #undef PHP_HTTP_RESPONSE_CODE + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseHeader, 0, 0, 0) + ZEND_ARG_INFO(0, header_name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getResponseHeader) + { + char *header_name_str = NULL; - int header_name_len = 0; ++ size_t header_name_len = 0; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &header_name_str, &header_name_len)) { + return; + } + if (header_name_str && header_name_len) { - char *header_value = php_http_env_get_response_header(header_name_str, header_name_len TSRMLS_CC); ++ char *header_value = php_http_env_get_response_header(header_name_str, header_name_len); + + if (header_value) { - RETURN_STRING(header_value, 0); ++ RETURN_STR(php_http_cs2zs(header_value, strlen(header_value))); + } + } else { + array_init(return_value); - php_http_env_get_response_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); ++ php_http_env_get_response_headers(Z_ARRVAL_P(return_value)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_getResponseCode, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, getResponseCode) + { + if (SUCCESS != zend_parse_parameters_none()) { + return; + } - RETURN_LONG(php_http_env_get_response_code(TSRMLS_C)); ++ RETURN_LONG(php_http_env_get_response_code()); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseHeader, 0, 0, 1) + ZEND_ARG_INFO(0, header_name) + ZEND_ARG_INFO(0, header_value) + ZEND_ARG_INFO(0, response_code) + ZEND_ARG_INFO(0, replace_header) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, setResponseHeader) + { + char *header_name_str; - int header_name_len; ++ size_t header_name_len; + zval *header_value = NULL; - long code = 0; ++ zend_long code = 0; + zend_bool replace_header = 1; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!lb", &header_name_str, &header_name_len, &header_value, &code, &replace_header)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!lb", &header_name_str, &header_name_len, &header_value, &code, &replace_header)) { + return; + } - RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header TSRMLS_CC)); ++ RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_setResponseCode, 0, 0, 1) + ZEND_ARG_INFO(0, code) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, setResponseCode) + { - long code; ++ zend_long code; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l", &code)) { + return; + } - RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code TSRMLS_CC)); ++ RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateLanguage, 0, 0, 1) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(1, result_array) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, negotiateLanguage) + { + HashTable *supported; + zval *rs_array = NULL; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) { + return; + } + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + + PHP_HTTP_DO_NEGOTIATE(language, supported, rs_array); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateCharset, 0, 0, 1) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(1, result_array) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, negotiateCharset) + { + HashTable *supported; + zval *rs_array = NULL; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) { + return; + } + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + PHP_HTTP_DO_NEGOTIATE(charset, supported, rs_array); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateEncoding, 0, 0, 1) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(1, result_array) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, negotiateEncoding) + { + HashTable *supported; + zval *rs_array = NULL; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) { + return; + } + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + PHP_HTTP_DO_NEGOTIATE(encoding, supported, rs_array); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiateContentType, 0, 0, 1) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(1, result_array) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, negotiateContentType) + { + HashTable *supported; + zval *rs_array = NULL; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) { + return; + } + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + PHP_HTTP_DO_NEGOTIATE(content_type, supported, rs_array); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnv_negotiate, 0, 0, 2) + ZEND_ARG_INFO(0, params) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(0, primary_type_separator) + ZEND_ARG_INFO(1, result_array) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnv, negotiate) + { + HashTable *supported, *rs; + zval *rs_array = NULL; + char *value_str, *sep_str = NULL; - int value_len, sep_len = 0; ++ size_t value_len, sep_len = 0; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) { + return; + } + - + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + - if ((rs = php_http_negotiate(value_str, value_len, supported, sep_str, sep_len TSRMLS_CC))) { ++ if ((rs = php_http_negotiate(value_str, value_len, supported, sep_str, sep_len))) { + PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array); + } else { + PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); + } + } + + static zend_function_entry php_http_env_methods[] = { + PHP_ME(HttpEnv, getRequestHeader, ai_HttpEnv_getRequestHeader, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, getRequestBody, ai_HttpEnv_getRequestBody, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + PHP_ME(HttpEnv, getResponseStatusForCode, ai_HttpEnv_getResponseStatusForCode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, getResponseStatusForAllCodes, ai_HttpEnv_getResponseStatusForAllCodes, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + PHP_ME(HttpEnv, getResponseHeader, ai_HttpEnv_getResponseHeader, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, getResponseCode, ai_HttpEnv_getResponseCode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, setResponseHeader, ai_HttpEnv_setResponseHeader, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, setResponseCode, ai_HttpEnv_setResponseCode, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + PHP_ME(HttpEnv, negotiateLanguage, ai_HttpEnv_negotiateLanguage, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, negotiateContentType, ai_HttpEnv_negotiateContentType, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, negotiateEncoding, ai_HttpEnv_negotiateEncoding, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, negotiateCharset, ai_HttpEnv_negotiateCharset, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(HttpEnv, negotiate, ai_HttpEnv_negotiate, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_env_class_entry; + + PHP_MINIT_FUNCTION(http_env) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Env", php_http_env_methods); - php_http_env_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_env_class_entry = zend_register_internal_class(&ce); + + return SUCCESS; + } + + + /* + * 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 --cc src/php_http_env.h index 0000000,3fc80ab..273ba16 mode 000000,100644..100644 --- a/src/php_http_env.h +++ b/src/php_http_env.h @@@ -1,0 -1,88 +1,93 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_ENV_H + #define PHP_HTTP_ENV_H + + #include "php_http_message_body.h" + #include "php_http_version.h" + + struct php_http_env_globals { + zval *server_var; + char *etag_mode; + + struct { + HashTable *headers; + php_http_message_body_t *body; + } request; + }; + + typedef enum php_http_content_encoding { + PHP_HTTP_CONTENT_ENCODING_NONE, + PHP_HTTP_CONTENT_ENCODING_GZIP + } php_http_content_encoding_t; + + typedef enum php_http_range_status { + PHP_HTTP_RANGE_NO, + PHP_HTTP_RANGE_OK, + PHP_HTTP_RANGE_ERR + } php_http_range_status_t; + -PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length, php_http_message_t *request TSRMLS_DC); -PHP_HTTP_API void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC); -PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request TSRMLS_DC); -PHP_HTTP_API int php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request TSRMLS_DC); -PHP_HTTP_API php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D); -PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC); ++PHP_HTTP_API php_http_range_status_t php_http_env_get_request_ranges(HashTable *ranges, size_t entity_length, php_http_message_t *request); ++PHP_HTTP_API void php_http_env_get_request_headers(HashTable *headers); ++PHP_HTTP_API char *php_http_env_get_request_header(const char *name_str, size_t name_len, size_t *len, php_http_message_t *request); ++PHP_HTTP_API zend_bool php_http_env_got_request_header(const char *name_str, size_t name_len, php_http_message_t *request); ++PHP_HTTP_API php_http_message_body_t *php_http_env_get_request_body(void); ++PHP_HTTP_API const char *php_http_env_get_request_method(php_http_message_t *request); + + typedef enum php_http_content_disposition { + PHP_HTTP_CONTENT_DISPOSITION_NONE, + PHP_HTTP_CONTENT_DISPOSITION_INLINE, + PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT + } php_http_content_disposition_t; + + typedef enum php_http_cache_status { + PHP_HTTP_CACHE_NO, + PHP_HTTP_CACHE_HIT, + PHP_HTTP_CACHE_MISS + } php_http_cache_status_t; + -PHP_HTTP_API long php_http_env_get_response_code(TSRMLS_D); ++PHP_HTTP_API long php_http_env_get_response_code(void); + PHP_HTTP_API const char *php_http_env_get_response_status_for_code(unsigned code); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht TSRMLS_DC); -PHP_HTTP_API char *php_http_env_get_response_header(const char *name_str, size_t name_len TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_code(long http_code TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace TSRMLS_DC, const char *fmt, ...); -PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv TSRMLS_DC); - -PHP_HTTP_API zval *php_http_env_get_server_var(const char *key_str, size_t key_len, zend_bool check TSRMLS_DC); -#define php_http_env_got_server_var(v) (NULL != php_http_env_get_server_var((v), strlen(v), 1 TSRMLS_CC)) -PHP_HTTP_API zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_get_response_headers(HashTable *headers_ht); ++PHP_HTTP_API char *php_http_env_get_response_header(const char *name_str, size_t name_len); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_code(long http_code); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header(long http_code, const char *header_str, size_t header_len, zend_bool replace); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_value(long http_code, const char *name_str, size_t name_len, zval *value, zend_bool replace); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_format(long http_code, zend_bool replace, const char *fmt, ...); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_env_set_response_header_va(long http_code, zend_bool replace, const char *fmt, va_list argv); ++ ++PHP_HTTP_API zval *php_http_env_get_server_var(const char *key_str, size_t key_len, zend_bool check); ++PHP_HTTP_API zval *php_http_env_get_superglobal(const char *key, size_t key_len); ++ ++static inline zend_bool php_http_env_got_server_var(const char *v) ++{ ++ return NULL != php_http_env_get_server_var(v, strlen(v), 1); ++} ++ + + PHP_HTTP_API zend_class_entry *php_http_env_class_entry; + PHP_MINIT_FUNCTION(http_env); + PHP_RSHUTDOWN_FUNCTION(http_env); + + #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 --cc src/php_http_env_request.c index 0000000,a884d2f..0ee68f4 mode 000000,100644..100644 --- a/src/php_http_env_request.c +++ b/src/php_http_env_request.c @@@ -1,0 -1,286 +1,275 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -static int grab_file(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) ++static int grab_file(zval *tmp_name, int argc, va_list argv, zend_hash_key *key) + { - zval *zfiles, **name, **zname, **error, **zerror, **type, **ztype, **size, **zsize, **tmp_name = zpp; ++ zval *zfiles, *name, *zname, *error, *zerror, *type, *ztype, *size, *zsize; + zend_hash_key *file_key; + + zfiles = (zval *) va_arg(argv, zval *); + file_key = (zend_hash_key *) va_arg(argv, zend_hash_key *); - name = (zval **) va_arg(argv, zval **); - size = (zval **) va_arg(argv, zval **); - type = (zval **) va_arg(argv, zval **); - error = (zval **) va_arg(argv, zval **); - - if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(name), key->h, (void *) &zname) - && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(size), key->h, (void *) &zsize) - && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(type), key->h, (void *) &ztype) - && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(error), key->h, (void *) &zerror) ++ name = (zval *) va_arg(argv, zval *); ++ size = (zval *) va_arg(argv, zval *); ++ type = (zval *) va_arg(argv, zval *); ++ error = (zval *) va_arg(argv, zval *); ++ ++ if ((zname = zend_hash_index_find(Z_ARRVAL_P(name), key->h)) ++ && (zsize = zend_hash_index_find(Z_ARRVAL_P(size), key->h)) ++ && (ztype = zend_hash_index_find(Z_ARRVAL_P(type), key->h)) ++ && (zerror = zend_hash_index_find(Z_ARRVAL_P(error), key->h)) + ) { - zval *entry, **array; - - MAKE_STD_ZVAL(entry); - array_init(entry); - - Z_ADDREF_PP(tmp_name); - add_assoc_zval_ex(entry, ZEND_STRS("file"), *tmp_name); - Z_ADDREF_PP(zname); - add_assoc_zval_ex(entry, ZEND_STRS("name"), *zname); - Z_ADDREF_PP(zsize); - add_assoc_zval_ex(entry, ZEND_STRS("size"), *zsize); - Z_ADDREF_PP(ztype); - add_assoc_zval_ex(entry, ZEND_STRS("type"), *ztype); - Z_ADDREF_PP(zerror); - add_assoc_zval_ex(entry, ZEND_STRS("error"), *zerror); - - if (SUCCESS == zend_hash_quick_find(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &array)) { - add_next_index_zval(*array, entry); ++ zval entry, *array; ++ ++ array_init(&entry); ++ ++ Z_TRY_ADDREF_P(tmp_name); ++ add_assoc_zval_ex(&entry, ZEND_STRL("file"), tmp_name); ++ Z_TRY_ADDREF_P(zname); ++ add_assoc_zval_ex(&entry, ZEND_STRL("name"), zname); ++ Z_TRY_ADDREF_P(zsize); ++ add_assoc_zval_ex(&entry, ZEND_STRL("size"), zsize); ++ Z_TRY_ADDREF_P(ztype); ++ add_assoc_zval_ex(&entry, ZEND_STRL("type"), ztype); ++ Z_TRY_ADDREF_P(zerror); ++ add_assoc_zval_ex(&entry, ZEND_STRL("error"), zerror); ++ ++ if (file_key->key && (array = zend_hash_find(Z_ARRVAL_P(zfiles), file_key->key))) { ++ add_next_index_zval(array, &entry); ++ } else if (!file_key->key && (array = zend_hash_index_find(Z_ARRVAL_P(zfiles), file_key->h))) { ++ add_next_index_zval(array, &entry); + } else { - zval *tmp; ++ zval tmp; + - MAKE_STD_ZVAL(tmp); - array_init(tmp); - add_next_index_zval(tmp, entry); - zend_hash_quick_update(Z_ARRVAL_P(zfiles), file_key->arKey, file_key->nKeyLength, file_key->h, (void *) &tmp, sizeof(zval *), NULL); ++ array_init(&tmp); ++ add_next_index_zval(&tmp, &entry); ++ if (file_key->key) { ++ zend_hash_update(Z_ARRVAL_P(zfiles), file_key->key, &tmp); ++ } else { ++ zend_hash_index_update(Z_ARRVAL_P(zfiles), file_key->h, &tmp); ++ } + } + } + + return ZEND_HASH_APPLY_KEEP; + } + -static int grab_files(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) ++static int grab_files(zval *val, int argc, va_list argv, zend_hash_key *key) + { - zval *zfiles, **name, **tmp_name, **error, **type, **size, **val = zpp; ++ zval *zfiles, *name, *tmp_name, *error, *type, *size; + + zfiles = (zval *) va_arg(argv, zval *); + - if (Z_TYPE_PP(val) == IS_ARRAY - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("tmp_name"), (void *) &tmp_name) - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("name"), (void *) &name) - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("size"), (void *) &size) - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("type"), (void *) &type) - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("error"), (void *) &error) ++ if ((Z_TYPE_P(val) == IS_ARRAY) ++ && (tmp_name = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("tmp_name"))) ++ && (name = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("name"))) ++ && (size = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("size"))) ++ && (type = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("type"))) ++ && (error = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("error"))) + ) { + int count; + - if (Z_TYPE_PP(tmp_name) == IS_ARRAY && (count = zend_hash_num_elements(Z_ARRVAL_PP(tmp_name))) > 1) { - if (count == zend_hash_num_elements(Z_ARRVAL_PP(name)) - && count == zend_hash_num_elements(Z_ARRVAL_PP(size)) - && count == zend_hash_num_elements(Z_ARRVAL_PP(type)) - && count == zend_hash_num_elements(Z_ARRVAL_PP(error)) ++ if (Z_TYPE_P(tmp_name) == IS_ARRAY && (count = zend_hash_num_elements(Z_ARRVAL_P(tmp_name))) > 1) { ++ if (count == zend_hash_num_elements(Z_ARRVAL_P(name)) ++ && count == zend_hash_num_elements(Z_ARRVAL_P(size)) ++ && count == zend_hash_num_elements(Z_ARRVAL_P(type)) ++ && count == zend_hash_num_elements(Z_ARRVAL_P(error)) + ) { - zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp_name) TSRMLS_CC, grab_file, 6, zfiles, key, name, size, type, error); ++ zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp_name), grab_file, 6, zfiles, key, name, size, type, error); + } else { + /* wat?! */ + return ZEND_HASH_APPLY_STOP; + } + } else { - zval *cpy, **tmp; - - MAKE_STD_ZVAL(cpy); - MAKE_COPY_ZVAL(val, cpy); - if (SUCCESS == zend_hash_find(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), (void *) &tmp)) { - Z_ADDREF_PP(tmp); - add_assoc_zval_ex(cpy, ZEND_STRS("file"), *tmp); - zend_hash_del_key_or_index(Z_ARRVAL_P(cpy), ZEND_STRS("tmp_name"), 0, HASH_DEL_KEY); ++ zval *tmp, entry; ++ ++ ZVAL_DUP(&entry, val); ++ if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("tmp_name")))) { ++ Z_ADDREF_P(tmp); ++ add_assoc_zval_ex(&entry, ZEND_STRL("file"), tmp); ++ zend_hash_str_del(Z_ARRVAL(entry), ZEND_STRL("tmp_name")); + } - if (key->nKeyLength > 0) { - zend_hash_quick_update(Z_ARRVAL_P(zfiles), key->arKey, key->nKeyLength, key->h, (void *) &cpy, sizeof(zval *), NULL); ++ if (key->key) { ++ zend_hash_update(Z_ARRVAL_P(zfiles), key->key, &entry); + } else { - zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, (void *) &cpy, sizeof(zval *), NULL); ++ zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, &entry); + } + } + } + + return ZEND_HASH_APPLY_KEEP; + } + + #define PHP_HTTP_ENV_REQUEST_OBJECT_INIT(obj) \ + do { \ + if (!obj->message) { \ - obj->message = php_http_message_init_env(NULL, PHP_HTTP_REQUEST TSRMLS_CC); \ ++ obj->message = php_http_message_init_env(NULL, PHP_HTTP_REQUEST); \ + } \ + } while(0) + + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest___construct, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvRequest, __construct) + { + php_http_message_object_t *obj; - zval *zsg, *zqs; ++ zval *zsg, zqs; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + obj->body = NULL; + - php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST TSRMLS_CC), unexpected_val, return); ++ php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST), unexpected_val, return); + - zsg = php_http_env_get_superglobal(ZEND_STRL("_GET") TSRMLS_CC); - MAKE_STD_ZVAL(zqs); - object_init_ex(zqs, php_http_querystring_class_entry); - php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val, - zval_ptr_dtor(&zqs); - return; - ); - zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), zqs TSRMLS_CC); ++ zsg = php_http_env_get_superglobal(ZEND_STRL("_GET")); ++ object_init_ex(&zqs, php_http_querystring_class_entry); ++ php_http_expect(SUCCESS == php_http_querystring_ctor(&zqs, zsg), unexpected_val, return); ++ zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), &zqs); + zval_ptr_dtor(&zqs); + - zsg = php_http_env_get_superglobal(ZEND_STRL("_POST") TSRMLS_CC); - MAKE_STD_ZVAL(zqs); - object_init_ex(zqs, php_http_querystring_class_entry); - php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val, - zval_ptr_dtor(&zqs); - return; - ); - zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), zqs TSRMLS_CC); ++ zsg = php_http_env_get_superglobal(ZEND_STRL("_POST")); ++ object_init_ex(&zqs, php_http_querystring_class_entry); ++ php_http_expect(SUCCESS == php_http_querystring_ctor(&zqs, zsg), unexpected_val, return); ++ zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), &zqs); + zval_ptr_dtor(&zqs); + - zsg = php_http_env_get_superglobal(ZEND_STRL("_COOKIE") TSRMLS_CC); - MAKE_STD_ZVAL(zqs); - object_init_ex(zqs, php_http_querystring_class_entry); - php_http_expect(SUCCESS == php_http_querystring_ctor(zqs, zsg TSRMLS_CC), unexpected_val, - zval_ptr_dtor(&zqs); - return; - ); - zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), zqs TSRMLS_CC); ++ zsg = php_http_env_get_superglobal(ZEND_STRL("_COOKIE")); ++ object_init_ex(&zqs, php_http_querystring_class_entry); ++ php_http_expect(SUCCESS == php_http_querystring_ctor(&zqs, zsg), unexpected_val, return); ++ zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), &zqs); + zval_ptr_dtor(&zqs); + - MAKE_STD_ZVAL(zqs); - array_init(zqs); - if ((zsg = php_http_env_get_superglobal(ZEND_STRL("_FILES") TSRMLS_CC))) { - zend_hash_apply_with_arguments(Z_ARRVAL_P(zsg) TSRMLS_CC, grab_files, 1, zqs); ++ array_init(&zqs); ++ if ((zsg = php_http_env_get_superglobal(ZEND_STRL("_FILES")))) { ++ zend_hash_apply_with_arguments(Z_ARRVAL_P(zsg), grab_files, 1, &zqs); + } - zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), zqs TSRMLS_CC); ++ zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), &zqs); + zval_ptr_dtor(&zqs); + } + + #define call_querystring_get(prop) \ + do {\ + zend_fcall_info fci; \ + zend_fcall_info_cache fcc; \ - zval *rv = NULL, mn, ***args = ecalloc(sizeof(zval **), ZEND_NUM_ARGS()); \ - zval *qs = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL(prop), 0 TSRMLS_CC); \ ++ zval rv, mn, *args = ecalloc(sizeof(zval), ZEND_NUM_ARGS()); \ ++ zval *this_ptr = getThis(); \ ++ zval qs_tmp, *qs = zend_read_property(Z_OBJCE_P(this_ptr), this_ptr, ZEND_STRL(prop), 0, &qs_tmp); \ + \ - INIT_PZVAL(&mn); \ ++ ZVAL_NULL(&rv); \ + array_init(&mn); \ - Z_ADDREF_P(qs); \ ++ Z_TRY_ADDREF_P(qs); \ + add_next_index_zval(&mn, qs); \ - add_next_index_stringl(&mn, ZEND_STRL("get"), 1); \ - zend_fcall_info_init(&mn, 0, &fci, &fcc, NULL, NULL TSRMLS_CC); \ ++ add_next_index_stringl(&mn, ZEND_STRL("get")); \ ++ zend_fcall_info_init(&mn, 0, &fci, &fcc, NULL, NULL); \ + zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args); \ - zend_fcall_info_argp(&fci TSRMLS_CC, ZEND_NUM_ARGS(), args); \ - zend_fcall_info_call(&fci, &fcc, &rv, NULL TSRMLS_CC); \ ++ zend_fcall_info_argp(&fci, ZEND_NUM_ARGS(), args); \ ++ zend_fcall_info_call(&fci, &fcc, &rv, NULL); \ + zend_fcall_info_args_clear(&fci, 1); \ + efree(args); \ + zval_dtor(&mn); \ - if (rv) { \ - RETVAL_ZVAL(rv, 0, 1); \ - } \ ++ RETVAL_ZVAL(&rv, 0, 1); \ + } while(0); + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getForm, 0, 0, 0) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, defval) + ZEND_ARG_INFO(0, delete) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvRequest, getForm) + { + if (ZEND_NUM_ARGS()) { + call_querystring_get("form"); + } else { - zval *zform = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), 0 TSRMLS_CC); ++ zval zform_tmp, *zform = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("form"), 0, &zform_tmp); + RETURN_ZVAL(zform, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getQuery, 0, 0, 0) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, defval) + ZEND_ARG_INFO(0, delete) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvRequest, getQuery) + { + if (ZEND_NUM_ARGS()) { + call_querystring_get("query"); + } else { - zval *zquery = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), 0 TSRMLS_CC); ++ zval zquery_tmp, *zquery = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("query"), 0, &zquery_tmp); + RETURN_ZVAL(zquery, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getCookie, 0, 0, 0) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, defval) + ZEND_ARG_INFO(0, delete) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvRequest, getCookie) + { + if (ZEND_NUM_ARGS()) { + call_querystring_get("cookie"); + } else { - zval *zcookie = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), 0 TSRMLS_CC); ++ zval zcookie_tmp, *zcookie = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("cookie"), 0, &zcookie_tmp); + RETURN_ZVAL(zcookie, 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvRequest_getFiles, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvRequest, getFiles) + { + if (SUCCESS == zend_parse_parameters_none()) { - zval *zfiles = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), 0 TSRMLS_CC); ++ zval zfiles_tmp, *zfiles = zend_read_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), 0, &zfiles_tmp); + RETURN_ZVAL(zfiles, 1, 0); + } + } + + static zend_function_entry php_http_env_request_methods[] = { + PHP_ME(HttpEnvRequest, __construct, ai_HttpEnvRequest___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpEnvRequest, getForm, ai_HttpEnvRequest_getForm, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvRequest, getQuery, ai_HttpEnvRequest_getQuery, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvRequest, getCookie, ai_HttpEnvRequest_getCookie, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvRequest, getFiles, ai_HttpEnvRequest_getFiles, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_env_request_class_entry; + + PHP_MINIT_FUNCTION(http_env_request) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Request", php_http_env_request_methods); - php_http_env_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC); ++ php_http_env_request_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry); + - zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("query"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("form"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("cookie"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("files"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("query"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("form"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("cookie"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_request_class_entry, ZEND_STRL("files"), ZEND_ACC_PROTECTED); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_env_response.c index 0000000,20a31eb..9bc3f5b mode 000000,100644..100644 --- a/src/php_http_env_response.c +++ b/src/php_http_env_response.c @@@ -1,0 -1,1513 +1,1453 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len TSRMLS_DC) ++static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len) + { + if (Z_TYPE_P(options) == IS_OBJECT) { + if (value_ptr) { + switch (type) { + case IS_DOUBLE: - zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr TSRMLS_CC); ++ zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr); + break; + case IS_LONG: - zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC); ++ zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(zend_long *)value_ptr); + break; + case IS_STRING: - zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC); ++ zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len); + break; + case IS_ARRAY: + case IS_OBJECT: - zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC); ++ zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr); + break; + } + } else { - zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC); ++ zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len); + } + } else { + convert_to_array(options); + if (value_ptr) { + switch (type) { + case IS_DOUBLE: - add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr); ++ add_assoc_double_ex(options, name_str, name_len, *(double *)value_ptr); + break; + case IS_LONG: - add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr); ++ add_assoc_long_ex(options, name_str, name_len, *(zend_long *)value_ptr); + break; + case IS_STRING: { - char *value = estrndup(value_ptr, value_len); - add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0); ++ zend_string *value = zend_string_init(value_ptr, value_len, 0); ++ add_assoc_str_ex(options, name_str, name_len, value); + break; + case IS_ARRAY: + case IS_OBJECT: + Z_ADDREF_P(value_ptr); - add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr); ++ add_assoc_zval_ex(options, name_str, name_len, value_ptr); + break; + } + } + } else { - add_assoc_null_ex(options, name_str, name_len + 1); ++ add_assoc_null_ex(options, name_str, name_len); + } + } + } -static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC) ++static zval *get_option(zval *options, const char *name_str, size_t name_len, zval *tmp) + { - zval *val, **valptr; ++ zval *val = NULL; + + if (Z_TYPE_P(options) == IS_OBJECT) { - val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC); ++ val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0, tmp); ++ } else if (Z_TYPE_P(options) == IS_ARRAY) { ++ val = zend_symtable_str_find(Z_ARRVAL_P(options), name_str, name_len); + } else { - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) { - val = *valptr; - } else { - val = NULL; - } ++ abort(); + } + if (val) { - Z_ADDREF_P(val); ++ Z_TRY_ADDREF_P(val); + } + return val; + } -static php_http_message_body_t *get_body(zval *options TSRMLS_DC) ++static php_http_message_body_t *get_body(zval *options) + { - zval *zbody; ++ zval zbody_tmp, *zbody; + php_http_message_body_t *body = NULL; + - if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) { - if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) { - php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC); ++ if ((zbody = get_option(options, ZEND_STRL("body"), &zbody_tmp))) { ++ if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry)) { ++ php_http_message_body_object_t *body_obj = PHP_HTTP_OBJ(NULL, zbody); + + body = body_obj->body; + } - zval_ptr_dtor(&zbody); ++ Z_TRY_DELREF_P(zbody); + } + + return body; + } -static php_http_message_t *get_request(zval *options TSRMLS_DC) ++static php_http_message_t *get_request(zval *options) + { - zval *zrequest; ++ zval zrequest_tmp, *zrequest; + php_http_message_t *request = NULL; + - if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) { - if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) { - php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC); ++ if ((zrequest = get_option(options, ZEND_STRL("request"), &zrequest_tmp))) { ++ if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry)) { ++ php_http_message_object_t *request_obj = PHP_HTTP_OBJ(NULL, zrequest); + + request = request_obj->message; + } - zval_ptr_dtor(&zrequest); ++ Z_TRY_DELREF_P(zrequest); + } + + return request; + } -static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC) ++static void set_cookie(zval *options, zval *zcookie_new) + { - HashPosition pos; - zval *zcookies_set; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - php_http_cookie_object_t *obj = zend_object_store_get_object(zcookie_new TSRMLS_CC); - - zcookies_set = get_option(options, ZEND_STRL("cookies") TSRMLS_CC); - if (!zcookies_set || Z_TYPE_P(zcookies_set) != IS_ARRAY) { - if (zcookies_set) { - zval_ptr_dtor(&zcookies_set); - } - MAKE_STD_ZVAL(zcookies_set); - array_init_size(zcookies_set, zend_hash_num_elements(&obj->list->cookies)); - } else { - SEPARATE_ZVAL(&zcookies_set); ++ zval tmp, zcookies_set_tmp, *zcookies_set; ++ php_http_arrkey_t key; ++ php_http_cookie_object_t *obj = PHP_HTTP_OBJ(NULL, zcookie_new); ++ ++ array_init(&tmp); ++ zcookies_set = get_option(options, ZEND_STRL("cookies"), &zcookies_set_tmp); ++ if (zcookies_set && Z_TYPE_P(zcookies_set) == IS_ARRAY) { ++ array_copy(Z_ARRVAL_P(zcookies_set), Z_ARRVAL(tmp)); ++ zval_ptr_dtor(zcookies_set); + } + - FOREACH_HASH_KEY(pos, &obj->list->cookies, key) { ++ ZEND_HASH_FOREACH_KEY(&obj->list->cookies, key.h, key.key) ++ { + Z_ADDREF_P(zcookie_new); - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_zval_ex(zcookies_set, key.str, key.len, zcookie_new); ++ if (key.key) { ++ add_assoc_zval_ex(&tmp, key.key->val, key.key->len, zcookie_new); + } else { - add_index_zval(zcookies_set, key.num, zcookie_new); ++ add_index_zval(&tmp, key.h, zcookie_new); + } + } ++ ZEND_HASH_FOREACH_END(); + - set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC); - zval_ptr_dtor(&zcookies_set); ++ set_option(options, ZEND_STRL("cookies"), IS_ARRAY, &tmp, 0); ++ zval_ptr_dtor(&tmp); + } + -php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC) ++php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request) + { + php_http_cache_status_t ret = PHP_HTTP_CACHE_NO; - int free_etag = 0; - char *header = NULL, *etag; ++ char *header = NULL, *etag = NULL; + php_http_message_body_t *body; - zval *zetag; ++ zval zetag_tmp, *zetag; + + - if (!(body = get_body(options TSRMLS_CC))) { ++ if (!(body = get_body(options))) { + return ret; + } + - if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) { - zval *zetag_copy = php_http_ztyp(IS_STRING, zetag); - zval_ptr_dtor(&zetag); - zetag = zetag_copy; ++ if ((zetag = get_option(options, ZEND_STRL("etag"), &zetag_tmp)) && Z_TYPE_P(zetag) != IS_NULL) { ++ zend_string *zs = zval_get_string(zetag); ++ etag = estrndup(zs->val, zs->len); ++ zend_string_release(zs); ++ zval_ptr_dtor(zetag); + } + - if (zetag && Z_STRLEN_P(zetag)) { - etag = Z_STRVAL_P(zetag); - } else if ((etag = php_http_message_body_etag(body))) { - set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC); - free_etag = 1; ++ if (!etag && (etag = php_http_message_body_etag(body))) { ++ set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag)); + } + - if (zetag) { - zval_ptr_dtor(&zetag); - } - - if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) { - ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD) ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS; - } - - if (free_etag) { - efree(etag); ++ if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request))) { ++ ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD) ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS; + } + ++ PTR_FREE(etag); + PTR_FREE(header); ++ + return ret; + } + -php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC) ++php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request) + { + php_http_cache_status_t ret = PHP_HTTP_CACHE_NO; + char *header; + time_t ums, lm = 0; + php_http_message_body_t *body; - zval *zlm; ++ zval zlm_tmp, *zlm; + - if (!(body = get_body(options TSRMLS_CC))) { ++ if (!(body = get_body(options))) { + return ret; + } + - if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) { - zval *zlm_copy = php_http_ztyp(IS_LONG, zlm); - zval_ptr_dtor(&zlm); - zlm = zlm_copy; ++ if ((zlm = get_option(options, ZEND_STRL("lastModified"), &zlm_tmp))) { ++ lm = zval_get_long(zlm); ++ zval_ptr_dtor(zlm); + } + - if (zlm && Z_LVAL_P(zlm) > 0) { - lm = Z_LVAL_P(zlm); - } else { ++ if (lm <= 0) { + lm = php_http_message_body_mtime(body); - set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC); - } - - if (zlm) { - zval_ptr_dtor(&zlm); ++ set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0); + } + - if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) { ++ if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request))) { + ums = php_parse_date(header, NULL); + + if (ums > 0 && ums >= lm) { + ret = PHP_HTTP_CACHE_HIT; + } else { + ret = PHP_HTTP_CACHE_MISS; + } + } + + PTR_FREE(header); + return ret; + } + + static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request) + { + long status = r->ops->get_status(r); - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (status && status / 100 != 2) { + return 0; + } + - if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) { ++ if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request)) { + return 0; + } + - if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) { ++ if (-1 == php_http_select_str(php_http_env_get_request_method(request), 2, "HEAD", "GET")) { + return 0; + } + + return 1; + } + -static size_t output(void *context, char *buf, size_t len TSRMLS_DC) ++static size_t output(void *context, char *buf, size_t len) + { + php_http_env_response_t *r = context; + + if (SUCCESS != r->ops->write(r, buf, len)) { + return (size_t) -1; + } + + /* we really only need to flush when throttling is enabled, + because we push the data as fast as possible anyway if not */ + if (r->throttle.delay >= PHP_HTTP_DIFFSEC) { + r->ops->flush(r); + php_http_sleep(r->throttle.delay); + } + return len; + } + -#define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0) + static ZEND_RESULT_CODE php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len) + { + size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (r->content.encoder) { + char *enc_str = NULL; + size_t enc_len = 0; + + if (buf) { + if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) { + return FAILURE; + } + } else { + if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) { + return FAILURE; + } + } + + if (!enc_str) { + return SUCCESS; + } - chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC); ++ chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r); + PTR_FREE(enc_str); + } else { - chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC); ++ chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r); + } + + return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE; + } + -php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC) ++static inline ZEND_RESULT_CODE php_http_env_response_send_done(php_http_env_response_t *r) ++{ ++ return php_http_env_response_send_data(r, NULL, 0); ++} ++ ++php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg) + { + zend_bool free_r; + + if ((free_r = !r)) { + r = emalloc(sizeof(*r)); + } + memset(r, 0, sizeof(*r)); + + if (ops) { + r->ops = ops; + } else { + r->ops = php_http_env_response_get_sapi_ops(); + } + + r->buffer = php_http_buffer_init(NULL); + - Z_ADDREF_P(options); - r->options = options; - - TSRMLS_SET_CTX(r->ts); ++ ZVAL_COPY(&r->options, options); + + if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) { + if (free_r) { + php_http_env_response_free(&r); + } else { + php_http_env_response_dtor(r); + r = NULL; + } + } + + return r; + } + + void php_http_env_response_dtor(php_http_env_response_t *r) + { + if (r->ops->dtor) { + r->ops->dtor(r); + } + php_http_buffer_free(&r->buffer); + zval_ptr_dtor(&r->options); + PTR_FREE(r->content.type); + PTR_FREE(r->content.encoding); + if (r->content.encoder) { + php_http_encoding_stream_free(&r->content.encoder); + } + } + + void php_http_env_response_free(php_http_env_response_t **r) + { + if (*r) { + php_http_env_response_dtor(*r); + efree(*r); + *r = NULL; + } + } + + static ZEND_RESULT_CODE php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request) + { + ZEND_RESULT_CODE ret = SUCCESS; - zval *zoption, *options = r->options; - TSRMLS_FETCH_FROM_CTX(r->ts); ++ zval zoption_tmp, *zoption, *options = &r->options; + + if (r->done) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) { ++ if ((zoption = get_option(options, ZEND_STRL("headers"), &zoption_tmp))) { + if (Z_TYPE_P(zoption) == IS_ARRAY) { - php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r TSRMLS_CC); ++ php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r); + } - zval_ptr_dtor(&zoption); ++ zval_ptr_dtor(zoption); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_LONG, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("responseCode"), &zoption_tmp))) { ++ zend_long rc = zval_get_long(zoption); + - zval_ptr_dtor(&zoption); - if (Z_LVAL_P(zoption_copy) > 0) { - ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy)); ++ zval_ptr_dtor(zoption); ++ if (rc > 0) { ++ ret = r->ops->set_status(r, rc); + } - zval_ptr_dtor(&zoption_copy); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) { ++ if ((zoption = get_option(options, ZEND_STRL("httpVersion"), &zoption_tmp))) { + php_http_version_t v; - zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); ++ zend_string *zs = zval_get_string(zoption); + - zval_ptr_dtor(&zoption); - if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) { ++ zval_ptr_dtor(zoption); ++ if (zs->len && php_http_version_parse(&v, zs->val)) { + ret = r->ops->set_protocol_version(r, &v); + php_http_version_dtor(&v); + } - zval_ptr_dtor(&zoption_copy); ++ zend_string_release(zs); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) { ++ if ((zoption = get_option(options, ZEND_STRL("cookies"), &zoption_tmp))) { + if (Z_TYPE_P(zoption) == IS_ARRAY) { - HashPosition pos; - zval **zcookie; ++ zval *zcookie; + - FOREACH_VAL(pos, zoption, zcookie) { - if (Z_TYPE_PP(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_PP(zcookie), php_http_cookie_class_entry TSRMLS_CC)) { - php_http_cookie_object_t *obj = zend_object_store_get_object(*zcookie TSRMLS_CC); ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zoption), zcookie) ++ { ++ if (Z_TYPE_P(zcookie) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zcookie), php_http_cookie_class_entry)) { ++ php_http_cookie_object_t *obj = PHP_HTTP_OBJ(NULL, zcookie); + char *str; + size_t len; + + php_http_cookie_list_to_string(obj->list, &str, &len); + if (SUCCESS != (ret = r->ops->add_header(r, "Set-Cookie: %s", str))) { + efree(str); + break; + } + efree(str); + } + } ++ ZEND_HASH_FOREACH_END(); + } - zval_ptr_dtor(&zoption); ++ zval_ptr_dtor(zoption); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("contentType"), &zoption_tmp))) { ++ zend_string *zs = zval_get_string(zoption); + - zval_ptr_dtor(&zoption); - if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) { - if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) { - r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy)); ++ zval_ptr_dtor(zoption); ++ if (zs->len && strchr(zs->val, '/')) { ++ if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", zs->len, zs->val))) { ++ r->content.type = estrndup(zs->val, zs->len); + } + } - zval_ptr_dtor(&zoption_copy); ++ zend_string_release(zs); + } + + if (ret != SUCCESS) { + return ret; + } + + if (r->range.status == PHP_HTTP_RANGE_OK) { + if (zend_hash_num_elements(&r->range.values) == 1) { - zval **range, **begin, **end; ++ zval *range, *begin, *end; + - if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range) - && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end) ++ if ( 1 == php_http_array_list(&r->range.values, 1, &range) ++ && 2 == php_http_array_list(Z_ARRVAL_P(range), 2, &begin, &end) + ) { + if (SUCCESS == (ret = r->ops->set_status(r, 206))) { - ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length); ++ ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_P(begin), Z_LVAL_P(end), r->content.length); + } + } else { + /* this should never happen */ + zend_hash_destroy(&r->range.values); + ret = FAILURE; + } + } else { - php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC); ++ php_http_boundary(r->range.boundary, sizeof(r->range.boundary)); + if (SUCCESS == (ret = r->ops->set_status(r, 206))) { + ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary); + } + } + } else { - if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("cacheControl"), &zoption_tmp))) { ++ zend_string *zs = zval_get_string(zoption); + - zval_ptr_dtor(&zoption); - if (Z_STRLEN_P(zoption_copy)) { - ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)); ++ zval_ptr_dtor(zoption); ++ if (zs->len) { ++ ret = r->ops->set_header(r, "Cache-Control: %.*s", zs->len, zs->val); + } - zval_ptr_dtor(&zoption_copy); ++ zend_string_release(zs); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption); - php_http_buffer_t buf; ++ if ((zoption = get_option(options, ZEND_STRL("contentDisposition"), &zoption_tmp))) { ++ ++ if (Z_TYPE_P(zoption) == IS_ARRAY) { ++ php_http_buffer_t buf; + - php_http_buffer_init(&buf); - if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) { - if (buf.used) { - ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data); ++ php_http_buffer_init(&buf); ++ if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT)) { ++ if (buf.used) { ++ ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data); ++ } + } - } + - php_http_buffer_dtor(&buf); - zval_ptr_dtor(&zoption_copy); - zval_ptr_dtor(&zoption); ++ php_http_buffer_dtor(&buf); ++ } ++ zval_ptr_dtor(zoption); + } + + if (ret != SUCCESS) { + return ret; + } + - if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_LONG, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("contentEncoding"), &zoption_tmp))) { ++ zend_long ce = zval_get_long(zoption); + zval zsupported; + HashTable *result = NULL; + - zval_ptr_dtor(&zoption); - switch (Z_LVAL_P(zoption_copy)) { ++ zval_ptr_dtor(zoption); ++ switch (ce) { + case PHP_HTTP_CONTENT_ENCODING_GZIP: - INIT_PZVAL(&zsupported); + array_init(&zsupported); - add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1); - add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1); - add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1); ++ add_next_index_stringl(&zsupported, ZEND_STRL("none")); ++ add_next_index_stringl(&zsupported, ZEND_STRL("gzip")); ++ add_next_index_stringl(&zsupported, ZEND_STRL("deflate")); + - if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) { - char *key_str = NULL; - uint key_len = 0; ++ if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request))) { ++ zend_string *key_str = NULL; ++ zend_ulong index = 0; + + zend_hash_internal_pointer_reset(result); - if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) { - if (!strcmp(key_str, "gzip")) { - if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) { ++ if (HASH_KEY_IS_STRING == zend_hash_get_current_key(result, &key_str, &index)) { ++ if (zend_string_equals_literal(key_str, "gzip")) { ++ if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP))) { + ret = FAILURE; + } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) { - r->content.encoding = estrndup(key_str, key_len - 1); ++ r->content.encoding = estrndup(key_str->val, key_str->len); + } - } else if (!strcmp(key_str, "deflate")) { - if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) { ++ } else if (zend_string_equals_literal(key_str, "deflate")) { ++ if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB))) { + ret = FAILURE; + } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) { - r->content.encoding = estrndup(key_str, key_len - 1); ++ r->content.encoding = estrndup(key_str->val, key_str->len); + } + } else { + ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding")); + } + + if (SUCCESS == ret) { + ret = r->ops->add_header(r, "Vary: Accept-Encoding"); + } + } + + zend_hash_destroy(result); + FREE_HASHTABLE(result); + } + + zval_dtor(&zsupported); + break; + + case PHP_HTTP_CONTENT_ENCODING_NONE: + default: + ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding")); + break; + } - zval_ptr_dtor(&zoption_copy); + } + + if (SUCCESS != ret) { + return ret; + } + + if (php_http_env_response_is_cacheable(r, request)) { - switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) { ++ switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request)) { + case PHP_HTTP_CACHE_MISS: + break; + + case PHP_HTTP_CACHE_NO: - if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) { ++ if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request)) { + break; + } + /* no break */ + + case PHP_HTTP_CACHE_HIT: + ret = r->ops->set_status(r, 304); + r->done = 1; + break; + } + - if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("etag"), &zoption_tmp))) { ++ zend_string *zs = zval_get_string(zoption); + - zval_ptr_dtor(&zoption); - if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) { - ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy)); ++ zval_ptr_dtor(zoption); ++ if (*zs->val != '"' && strncmp(zs->val, "W/\"", 3)) { ++ ret = r->ops->set_header(r, "ETag: \"%s\"", zs->val); + } else { - ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy)); ++ ret = r->ops->set_header(r, "ETag: %s", zs->val); + } - zval_ptr_dtor(&zoption_copy); ++ zend_string_release(zs); + } - if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_LONG, zoption); ++ if ((zoption = get_option(options, ZEND_STRL("lastModified"), &zoption_tmp))) { ++ zend_long lm = zval_get_long(zoption); + - zval_ptr_dtor(&zoption); - if (Z_LVAL_P(zoption_copy)) { - char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC); ++ zval_ptr_dtor(zoption); ++ if (lm) { ++ zend_string *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), lm, 0); + if (date) { - ret = r->ops->set_header(r, "Last-Modified: %s", date); - efree(date); ++ ret = r->ops->set_header(r, "Last-Modified: %s", date->val); ++ zend_string_release(date); + } + } - zval_ptr_dtor(&zoption_copy); + } + } + } + + return ret; + } + + static ZEND_RESULT_CODE php_http_env_response_send_body(php_http_env_response_t *r) + { + ZEND_RESULT_CODE ret = SUCCESS; - zval *zoption; ++ zval zoption_tmp, *zoption; + php_http_message_body_t *body; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (r->done) { + return ret; + } + - if ((body = get_body(r->options TSRMLS_CC))) { - if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) { - if (Z_TYPE_P(zoption) == IS_DOUBLE) { - r->throttle.delay = Z_DVAL_P(zoption); - } - zval_ptr_dtor(&zoption); ++ if ((body = get_body(&r->options))) { ++ if ((zoption = get_option(&r->options, ZEND_STRL("throttleDelay"), &zoption_tmp))) { ++ r->throttle.delay = zval_get_double(zoption); ++ zval_ptr_dtor(zoption); + } - if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) { - if (Z_TYPE_P(zoption) == IS_LONG) { - r->throttle.chunk = Z_LVAL_P(zoption); - } - zval_ptr_dtor(&zoption); ++ if ((zoption = get_option(&r->options, ZEND_STRL("throttleChunk"), &zoption_tmp))) { ++ r->throttle.chunk = zval_get_long(zoption); ++ zval_ptr_dtor(zoption); + } + + if (r->range.status == PHP_HTTP_RANGE_OK) { + if (zend_hash_num_elements(&r->range.values) == 1) { + /* single range */ - zval **range, **begin, **end; ++ zval *range, *begin, *end; + - if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range) - && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end) ++ if ( 1 == php_http_array_list(&r->range.values, 1, &range) ++ && 2 == php_http_array_list(Z_ARRVAL_P(range), 2, &begin, &end) + ) { + /* send chunk */ - ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1); ++ ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_P(begin), Z_LVAL_P(end) - Z_LVAL_P(begin) + 1); + if (ret == SUCCESS) { + ret = php_http_env_response_send_done(r); + } + zend_hash_destroy(&r->range.values); + } else { + /* this should never happen */ + zend_hash_destroy(&r->range.values); + r->ops->set_status(r, 500); + ret = FAILURE; + } + + } else { + /* send multipart/byte-ranges message */ - HashPosition pos; - zval **chunk; ++ zval *chunk; + - FOREACH_HASH_VAL(pos, &r->range.values, chunk) { - zval **begin, **end; ++ ZEND_HASH_FOREACH_VAL(&r->range.values, chunk) ++ { ++ zval *begin, *end; + - if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) { ++ if (2 == php_http_array_list(Z_ARRVAL_P(chunk), 2, &begin, &end)) { + php_http_buffer_appendf(r->buffer, + PHP_HTTP_CRLF + "--%s" PHP_HTTP_CRLF + "Content-Type: %s" PHP_HTTP_CRLF + "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF, + /* - */ + r->range.boundary, + r->content.type ? r->content.type : "application/octet-stream", - Z_LVAL_PP(begin), - Z_LVAL_PP(end), ++ Z_LVAL_P(begin), ++ Z_LVAL_P(end), + r->content.length + ); - ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1); ++ ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_P(begin), Z_LVAL_P(end) - Z_LVAL_P(begin) + 1); + } + } ++ ZEND_HASH_FOREACH_END(); + + if (ret == SUCCESS) { + php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary); + ret = php_http_env_response_send_done(r); + } + zend_hash_destroy(&r->range.values); + } + + } else { + ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0); + if (ret == SUCCESS) { + ret = php_http_env_response_send_done(r); + } + } + } + return ret; + } + + ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r) + { + php_http_message_t *request; + php_http_message_body_t *body; - TSRMLS_FETCH_FROM_CTX(r->ts); + - request = get_request(r->options TSRMLS_CC); ++ request = get_request(&r->options); + + /* check for ranges */ - if ((body = get_body(r->options TSRMLS_CC))) { ++ if ((body = get_body(&r->options))) { + r->content.length = php_http_message_body_size(body); + + if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) { + return FAILURE; + } else { - zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0); - r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC); ++ ZEND_INIT_SYMTABLE_EX(&r->range.values, 0, 0); ++ r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request); + + switch (r->range.status) { + case PHP_HTTP_RANGE_NO: + zend_hash_destroy(&r->range.values); + break; + + case PHP_HTTP_RANGE_ERR: - if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) { ++ if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request)) { + r->range.status = PHP_HTTP_RANGE_NO; + zend_hash_destroy(&r->range.values); + } else { + r->done = 1; + zend_hash_destroy(&r->range.values); + if (SUCCESS != r->ops->set_status(r, 416)) { + return FAILURE; + } + if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) { + return FAILURE; + } + } + break; + + case PHP_HTTP_RANGE_OK: - if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC) - || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC) ++ if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(&r->options, ZEND_STRL("If-Range"), request) ++ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(&r->options, ZEND_STRL("If-Range"), request) + ) { + r->range.status = PHP_HTTP_RANGE_NO; + zend_hash_destroy(&r->range.values); + break; + } - if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC) - || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC) - || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC) ++ if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(&r->options, ZEND_STRL("If-Match"), request) ++ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(&r->options, ZEND_STRL("If-Unmodified-Since"), request) ++ || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(&r->options, ZEND_STRL("Unless-Modified-Since"), request) + ) { + r->done = 1; + zend_hash_destroy(&r->range.values); + if (SUCCESS != r->ops->set_status(r, 412)) { + return FAILURE; + } + break; + } + + break; + } + } + } + + if (SUCCESS != php_http_env_response_send_head(r, request)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers"); ++ php_error_docref(NULL, E_WARNING, "Failed to send response headers"); + return FAILURE; + } + + if (SUCCESS != php_http_env_response_send_body(r)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body"); ++ php_error_docref(NULL, E_WARNING, "Failed to send response body"); + return FAILURE; + } + + if (SUCCESS != r->ops->finish(r)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response"); ++ php_error_docref(NULL, E_WARNING, "Failed to finish response"); + return FAILURE; + } + + return SUCCESS; + } + + static long php_http_env_response_sapi_get_status(php_http_env_response_t *r) + { - TSRMLS_FETCH_FROM_CTX(r->ts); - - return php_http_env_get_response_code(TSRMLS_C); ++ return php_http_env_get_response_code(); + } + static ZEND_RESULT_CODE php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code) + { - TSRMLS_FETCH_FROM_CTX(r->ts); - - return php_http_env_set_response_code(http_code TSRMLS_CC); ++ return php_http_env_set_response_code(http_code); + } + static ZEND_RESULT_CODE php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v) + { - TSRMLS_FETCH_FROM_CTX(r->ts); + - return php_http_env_set_response_protocol_version(v TSRMLS_CC); ++ return php_http_env_set_response_protocol_version(v); + } + static ZEND_RESULT_CODE php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...) + { + ZEND_RESULT_CODE ret; + va_list args; - TSRMLS_FETCH_FROM_CTX(r->ts); + + va_start(args, fmt); - ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC); ++ ret = php_http_env_set_response_header_va(0, 1, fmt, args); + va_end(args); + + return ret; + } + static ZEND_RESULT_CODE php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...) + { + ZEND_RESULT_CODE ret; + va_list args; - TSRMLS_FETCH_FROM_CTX(r->ts); + + va_start(args, fmt); - ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC); ++ ret = php_http_env_set_response_header_va(0, 0, fmt, args); + va_end(args); + + return ret; + } + static ZEND_RESULT_CODE php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len) + { - TSRMLS_FETCH_FROM_CTX(r->ts); - - return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC); ++ return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1); + } + static ZEND_RESULT_CODE php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len) + { - TSRMLS_FETCH_FROM_CTX(r->ts); - + if (0 < PHPWRITE(data_str, data_len)) { + return SUCCESS; + } + return FAILURE; + } + static ZEND_RESULT_CODE php_http_env_response_sapi_flush(php_http_env_response_t *r) + { - TSRMLS_FETCH_FROM_CTX(r->ts); - -#if PHP_VERSION_ID >= 50400 - if (php_output_get_level(TSRMLS_C)) { - php_output_flush_all(TSRMLS_C); ++ if (php_output_get_level()) { ++ php_output_flush_all(); + } - if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) { - sapi_flush(TSRMLS_C); ++ if (!(php_output_get_status() & PHP_OUTPUT_IMPLICITFLUSH)) { ++ sapi_flush(); + } -#else - php_end_ob_buffer(1, 1 TSRMLS_CC); - sapi_flush(TSRMLS_C); -#endif + + return SUCCESS; + } + static ZEND_RESULT_CODE php_http_env_response_sapi_finish(php_http_env_response_t *r) + { + return SUCCESS; + } + + static php_http_env_response_ops_t php_http_env_response_sapi_ops = { + NULL, + NULL, + php_http_env_response_sapi_get_status, + php_http_env_response_sapi_set_status, + php_http_env_response_sapi_set_protocol_version, + php_http_env_response_sapi_set_header, + php_http_env_response_sapi_add_header, + php_http_env_response_sapi_del_header, + php_http_env_response_sapi_write, + php_http_env_response_sapi_flush, + php_http_env_response_sapi_finish + }; + + php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void) + { + return &php_http_env_response_sapi_ops; + } + + typedef struct php_http_env_response_stream_ctx { + HashTable header; + php_http_version_t version; + long status_code; + + php_stream *stream; + php_stream_filter *chunked_filter; + php_http_message_t *request; + + unsigned started:1; + unsigned finished:1; + unsigned chunked:1; + } php_http_env_response_stream_ctx_t; + + static ZEND_RESULT_CODE php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg) + { + php_http_env_response_stream_ctx_t *ctx; + size_t buffer_size = 0x1000; - TSRMLS_FETCH_FROM_CTX(r->ts); + + ctx = ecalloc(1, sizeof(*ctx)); + + ctx->stream = init_arg; - if (!ctx->stream || SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) { - efree(ctx); - return FAILURE; - } ++ ++GC_REFCOUNT(ctx->stream->res); ++ ZEND_INIT_SYMTABLE(&ctx->header); ++ php_http_version_init(&ctx->version, 1, 1); + php_stream_set_option(ctx->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buffer_size); - zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0); - php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC); + ctx->status_code = 200; + ctx->chunked = 1; - ctx->request = get_request(r->options TSRMLS_CC); ++ ctx->request = get_request(&r->options); + + /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */ + if (ctx->request && ctx->request->http.version.major == 1 && ctx->request->http.version.minor == 0) { + ctx->version.minor = 0; + } + + r->ctx = ctx; + + return SUCCESS; + } + static void php_http_env_response_stream_dtor(php_http_env_response_t *r) + { + php_http_env_response_stream_ctx_t *ctx = r->ctx; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (ctx->chunked_filter) { - ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC); ++ ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1); + } + zend_hash_destroy(&ctx->header); - zend_list_delete(ctx->stream->rsrc_id); ++ zend_list_delete(ctx->stream->res); + efree(ctx); + r->ctx = NULL; + } -static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf TSRMLS_DC) ++static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header, php_http_buffer_t *buf) + { - HashPosition pos; - zval **val; ++ zval *val; + - FOREACH_HASH_VAL(pos, header, val) { - if (Z_TYPE_PP(val) == IS_ARRAY) { - php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val), buf TSRMLS_CC); ++ ZEND_HASH_FOREACH_VAL(header, val) ++ { ++ if (Z_TYPE_P(val) == IS_ARRAY) { ++ php_http_env_response_stream_header(ctx, Z_ARRVAL_P(val), buf); + } else { - zval *tmp = php_http_ztyp(IS_STRING, *val); ++ zend_string *zs = zval_get_string(val); + + if (ctx->chunked) { + /* disable chunked transfer encoding if we've got an explicit content-length */ - if (!strncasecmp(Z_STRVAL_P(tmp), "Content-Length:", lenof("Content-Length:"))) { ++ if (!strncasecmp(zs->val, "Content-Length:", lenof("Content-Length:"))) { + ctx->chunked = 0; + } + } - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); ++ php_http_buffer_append(buf, zs->val, zs->len); + php_http_buffer_appends(buf, PHP_HTTP_CRLF); - zval_ptr_dtor(&tmp); ++ zend_string_release(zs); + } + } ++ ZEND_HASH_FOREACH_END(); + } -static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC) ++static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx) + { + php_http_buffer_t header_buf; + + if (ctx->started || ctx->finished) { + return FAILURE; + } + + php_http_buffer_init(&header_buf); + php_http_buffer_appendf(&header_buf, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code)); + + /* there are some limitations regarding TE:chunked, see https://tools.ietf.org/html/rfc7230#section-3.3.1 */ + if (ctx->version.major == 1 && ctx->version.minor == 0) { + ctx->chunked = 0; + } else if (ctx->status_code == 204 || ctx->status_code/100 == 1) { + ctx->chunked = 0; + } else if (ctx->request && ctx->status_code/100 == 2 && !strcasecmp(ctx->request->http.info.request.method, "CONNECT")) { + ctx->chunked = 0; + } + - php_http_env_response_stream_header(ctx, &ctx->header, &header_buf TSRMLS_CC); ++ php_http_env_response_stream_header(ctx, &ctx->header, &header_buf); + + /* enable chunked transfer encoding */ + if (ctx->chunked) { + php_http_buffer_appends(&header_buf, "Transfer-Encoding: chunked" PHP_HTTP_CRLF); + } ++ + php_http_buffer_appends(&header_buf, PHP_HTTP_CRLF); + + if (header_buf.used == php_stream_write(ctx->stream, header_buf.data, header_buf.used)) { + ctx->started = 1; + } + php_http_buffer_dtor(&header_buf); + php_stream_flush(ctx->stream); + + if (ctx->chunked) { - ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0 TSRMLS_CC); ++ ctx->chunked_filter = php_stream_filter_create("http.chunked_encode", NULL, 0); + php_stream_filter_append(&ctx->stream->writefilters, ctx->chunked_filter); + } + + return ctx->started ? SUCCESS : FAILURE; + } + static long php_http_env_response_stream_get_status(php_http_env_response_t *r) + { + php_http_env_response_stream_ctx_t *ctx = r->ctx; + + return ctx->status_code; + } + static ZEND_RESULT_CODE php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; + + if (stream_ctx->started || stream_ctx->finished) { + return FAILURE; + } + + stream_ctx->status_code = http_code; + + return SUCCESS; + } + static ZEND_RESULT_CODE php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; + + if (stream_ctx->started || stream_ctx->finished) { + return FAILURE; + } + + memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version)); + + return SUCCESS; + } + static ZEND_RESULT_CODE php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; + char *header_end, *header_str = NULL; + size_t header_len = 0; - zval *zheader, **zheader_ptr; ++ zval zheader, *zheader_ptr; ++ zend_string *header_key; ++ ZEND_RESULT_CODE rv; + + if (stream_ctx->started || stream_ctx->finished) { + return FAILURE; + } + + header_len = vspprintf(&header_str, 0, fmt, argv); + + if (!(header_end = strchr(header_str, ':'))) { + efree(header_str); + return FAILURE; + } + - *header_end = '\0'; ++ header_key = zend_string_init(header_str, header_end - header_str, 0); + - if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) { - convert_to_array(*zheader_ptr); - *header_end = ':'; - return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0); ++ if (!replace && (zheader_ptr = zend_hash_find(&stream_ctx->header, header_key))) { ++ convert_to_array(zheader_ptr); ++ rv = add_next_index_str(zheader_ptr, php_http_cs2zs(header_str, header_len)); + } else { - MAKE_STD_ZVAL(zheader); - ZVAL_STRINGL(zheader, header_str, header_len, 0); ++ ZVAL_STR(&zheader, php_http_cs2zs(header_str, header_len)); + - if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) { - zval_ptr_dtor(&zheader); - return FAILURE; - } - - *header_end = ':'; - return SUCCESS; ++ rv = zend_hash_update(&stream_ctx->header, header_key, &zheader) ++ ? SUCCESS : FAILURE; + } ++ ++ zend_string_release(header_key); ++ ++ return rv; + } + static ZEND_RESULT_CODE php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...) + { + ZEND_RESULT_CODE ret; + va_list argv; + + va_start(argv, fmt); + ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv); + va_end(argv); + + return ret; + } + static ZEND_RESULT_CODE php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...) + { + ZEND_RESULT_CODE ret; + va_list argv; + + va_start(argv, fmt); + ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv); + va_end(argv); + + return ret; + } + static ZEND_RESULT_CODE php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; + + if (stream_ctx->started || stream_ctx->finished) { + return FAILURE; + } + - zend_hash_del(&stream_ctx->header, header_str, header_len + 1); ++ zend_hash_str_del(&stream_ctx->header, header_str, header_len); + return SUCCESS; + } + static ZEND_RESULT_CODE php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (stream_ctx->finished) { + return FAILURE; + } + if (!stream_ctx->started) { - if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) { ++ if (SUCCESS != php_http_env_response_stream_start(stream_ctx)) { + return FAILURE; + } + } + + if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) { + return FAILURE; + } + + return SUCCESS; + } + static ZEND_RESULT_CODE php_http_env_response_stream_flush(php_http_env_response_t *r) + { + php_http_env_response_stream_ctx_t *stream_ctx = r->ctx; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (stream_ctx->finished) { + return FAILURE; + } + if (!stream_ctx->started) { - if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) { ++ if (SUCCESS != php_http_env_response_stream_start(stream_ctx)) { + return FAILURE; + } + } + + return php_stream_flush(stream_ctx->stream); + } + static ZEND_RESULT_CODE php_http_env_response_stream_finish(php_http_env_response_t *r) + { + php_http_env_response_stream_ctx_t *ctx = r->ctx; - TSRMLS_FETCH_FROM_CTX(r->ts); + + if (ctx->finished) { + return FAILURE; + } + if (!ctx->started) { - if (SUCCESS != php_http_env_response_stream_start(ctx TSRMLS_CC)) { ++ if (SUCCESS != php_http_env_response_stream_start(ctx)) { + return FAILURE; + } + } + + php_stream_flush(ctx->stream); + if (ctx->chunked && ctx->chunked_filter) { + php_stream_filter_flush(ctx->chunked_filter, 1); - ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1 TSRMLS_CC); ++ ctx->chunked_filter = php_stream_filter_remove(ctx->chunked_filter, 1); + } + + ctx->finished = 1; + + return SUCCESS; + } + + static php_http_env_response_ops_t php_http_env_response_stream_ops = { + php_http_env_response_stream_init, + php_http_env_response_stream_dtor, + php_http_env_response_stream_get_status, + php_http_env_response_stream_set_status, + php_http_env_response_stream_set_protocol_version, + php_http_env_response_stream_set_header, + php_http_env_response_stream_add_header, + php_http_env_response_stream_del_header, + php_http_env_response_stream_write, + php_http_env_response_stream_flush, + php_http_env_response_stream_finish + }; + + php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void) + { + return &php_http_env_response_stream_ops; + } + + #define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \ + do { \ + if (!obj->message) { \ - obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \ ++ obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE); \ + } \ + } while (0) + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, __construct) + { + php_http_message_object_t *obj; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + - php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), unexpected_val, return); ++ php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE), unexpected_val, return); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke, 0, 0, 1) + ZEND_ARG_INFO(0, ob_string) + ZEND_ARG_INFO(0, ob_flags) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, __invoke) + { + char *ob_str; - int ob_len; - long ob_flags = 0; ++ size_t ob_len; ++ zend_long ob_flags = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &ob_str, &ob_len, &ob_flags)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj); + + if (!obj->body) { + php_http_message_object_init_body_object(obj); + } - php_http_message_body_append(obj->message->body, ob_str, ob_len); -#if PHP_VERSION_ID >= 50400 ++ + if (ob_flags & PHP_OUTPUT_HANDLER_CLEAN) { + php_stream_truncate_set_size(php_http_message_body_stream(obj->message->body), 0); ++ } else { ++ php_http_message_body_append(obj->message->body, ob_str, ob_len); + } + RETURN_TRUE; -#else - RETURN_EMPTY_STRING(); -#endif + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, env_request, http\\Message, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setEnvRequest) + { + zval *env_req = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|O", &env_req, php_http_message_class_entry), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType, 0, 0, 1) + ZEND_ARG_INFO(0, content_type) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setContentType) + { + char *ct_str = NULL; - int ct_len = 0; ++ size_t ct_len = 0; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &ct_str, &ct_len), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, disposition_params, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setContentDisposition) + { + zval *zdisposition; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "a", &zdisposition), invalid_arg, return); + - zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC); ++ zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding, 0, 0, 1) + ZEND_ARG_INFO(0, content_encoding) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setContentEncoding) + { - long ce; ++ zend_long ce; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &ce), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl, 0, 0, 1) + ZEND_ARG_INFO(0, cache_control) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setCacheControl) + { + char *cc_str = NULL; - int cc_len = 0; ++ size_t cc_len = 0; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &cc_str, &cc_len), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified, 0, 0, 1) + ZEND_ARG_INFO(0, last_modified) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setLastModified) + { - long last_modified; ++ zend_long last_modified; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &last_modified), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified, 0, 0, 0) + ZEND_ARG_INFO(0, header_name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, isCachedByLastModified) + { + char *header_name_str = NULL; - int header_name_len = 0; ++ size_t header_name_len = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &header_name_str, &header_name_len)) { + if (!header_name_str || !header_name_len) { + header_name_str = "If-Modified-Since"; + header_name_len = lenof("If-Modified-Since"); + } + - RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC)); ++ RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis()))); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag, 0, 0, 1) + ZEND_ARG_INFO(0, etag) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setEtag) + { + char *etag_str = NULL; - int etag_len = 0; ++ size_t etag_len = 0; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &etag_str, &etag_len), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag, 0, 0, 0) + ZEND_ARG_INFO(0, header_name) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, isCachedByEtag) + { + char *header_name_str = NULL; - int header_name_len = 0; ++ size_t header_name_len = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &header_name_str, &header_name_len)) { + if (!header_name_str || !header_name_len) { + header_name_str = "If-None-Match"; + header_name_len = lenof("If-None-Match"); + } - RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC)); ++ RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis()))); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate, 0, 0, 1) + ZEND_ARG_INFO(0, chunk_size) + ZEND_ARG_INFO(0, delay) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setThrottleRate) + { - long chunk_size; ++ zend_long chunk_size; + double delay = 1; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l|d", &chunk_size, &delay), invalid_arg, return); + - set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC); - set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC); ++ set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0); ++ set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCookie, 0, 0, 1) + ZEND_ARG_INFO(0, cookie) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, setCookie) + { - zval *zcookie_new; ++ zval *zcookie_new, tmp; ++ zend_string *zs; + zend_error_handling zeh; + php_http_cookie_list_t *list = NULL; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zcookie_new), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zcookie_new), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh); + switch (Z_TYPE_P(zcookie_new)) { + case IS_OBJECT: - if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) { ++ if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry)) { + Z_ADDREF_P(zcookie_new); + break; + } + /* no break */ + case IS_ARRAY: - list = php_http_cookie_list_from_struct(NULL, zcookie_new TSRMLS_CC); - MAKE_STD_ZVAL(zcookie_new); - ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0); ++ list = php_http_cookie_list_from_struct(NULL, zcookie_new); ++ zcookie_new = &tmp; ++ ZVAL_OBJECT(zcookie_new, &php_http_cookie_object_new_ex(php_http_cookie_class_entry, list)->zo, 0); + break; + + default: - zcookie_new = php_http_ztyp(IS_STRING, zcookie_new); - list = php_http_cookie_list_parse(NULL, Z_STRVAL_P(zcookie_new), Z_STRLEN_P(zcookie_new), 0, NULL TSRMLS_CC); - zval_ptr_dtor(&zcookie_new); - MAKE_STD_ZVAL(zcookie_new); - ZVAL_OBJVAL(zcookie_new, php_http_cookie_object_new_ex(php_http_cookie_class_entry, list, NULL TSRMLS_CC), 0); ++ zs = zval_get_string(zcookie_new); ++ list = php_http_cookie_list_parse(NULL, zs->val, zs->len, 0, NULL); ++ zend_string_release(zs); ++ zcookie_new = &tmp; ++ ZVAL_OBJECT(zcookie_new, &php_http_cookie_object_new_ex(php_http_cookie_class_entry, list)->zo, 0); + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + - set_cookie(getThis(), zcookie_new TSRMLS_CC); - zval_ptr_dtor(&zcookie_new); ++ set_cookie(getThis(), zcookie_new); ++ zval_ptr_dtor(zcookie_new); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0) + ZEND_ARG_INFO(0, stream) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpEnvResponse, send) + { + zval *zstream = NULL; + php_stream *s = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|r", &zstream)) { + /* first flush the output layer to avoid conflicting headers and output; + * also, ob_start($thisEnvResponse) might have been called */ -#if PHP_VERSION_ID >= 50400 - php_output_end_all(TSRMLS_C); -#else - php_end_ob_buffers(1 TSRMLS_CC); -#endif ++ php_output_end_all(); + + if (zstream) { + php_http_env_response_t *r; + - php_stream_from_zval(s, &zstream); - r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC); ++ php_stream_from_zval(s, zstream); ++ r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s); + if (!r) { + RETURN_FALSE; + } + + RETVAL_BOOL(SUCCESS == php_http_env_response_send(r)); + php_http_env_response_free(&r); + } else { + php_http_env_response_t r; + - if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) { ++ if (!php_http_env_response_init(&r, getThis(), NULL, NULL)) { + RETURN_FALSE; + } + + RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r)); + php_http_env_response_dtor(&r); + } + } + } + + static zend_function_entry php_http_env_response_methods[] = { + PHP_ME(HttpEnvResponse, __construct, ai_HttpEnvResponse___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpEnvResponse, __invoke, ai_HttpEnvResponse___invoke, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setEnvRequest, ai_HttpEnvResponse_setEnvRequest, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setCookie, ai_HttpEnvResponse_setCookie, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setContentType, ai_HttpEnvResponse_setContentType, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setContentDisposition, ai_HttpEnvResponse_setContentDisposition, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setContentEncoding, ai_HttpEnvResponse_setContentEncoding, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setCacheControl, ai_HttpEnvResponse_setCacheControl, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setLastModified, ai_HttpEnvResponse_setLastModified, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, isCachedByLastModified, ai_HttpEnvResponse_isCachedByLastModified, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setEtag, ai_HttpEnvResponse_setEtag, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, isCachedByEtag, ai_HttpEnvResponse_isCachedByEtag, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, setThrottleRate, ai_HttpEnvResponse_setThrottleRate, ZEND_ACC_PUBLIC) + PHP_ME(HttpEnvResponse, send, ai_HttpEnvResponse_send, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_env_response_class_entry; + + PHP_MINIT_FUNCTION(http_env_response) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods); - php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC); - - zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC); - - zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC); - zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC); - zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC); - - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC); - zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry); ++ ++ zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE); ++ zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP); ++ ++ zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO); ++ zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT); ++ zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS); ++ ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cookies"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED); ++ zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED); + + return SUCCESS; + } + + + /* + * 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 --cc src/php_http_env_response.h index 0000000,e6a112f..ae1441d mode 000000,100644..100644 --- a/src/php_http_env_response.h +++ b/src/php_http_env_response.h @@@ -1,0 -1,90 +1,86 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_ENV_RESPONSE_H + #define PHP_HTTP_ENV_RESPONSE_H + + typedef struct php_http_env_response php_http_env_response_t; + + typedef struct php_http_env_response_ops { + ZEND_RESULT_CODE (*init)(php_http_env_response_t *r, void *arg); + void (*dtor)(php_http_env_response_t *r); + long (*get_status)(php_http_env_response_t *r); + ZEND_RESULT_CODE (*set_status)(php_http_env_response_t *r, long http_code); + ZEND_RESULT_CODE (*set_protocol_version)(php_http_env_response_t *r, php_http_version_t *v); + ZEND_RESULT_CODE (*set_header)(php_http_env_response_t *r, const char *fmt, ...); + ZEND_RESULT_CODE (*add_header)(php_http_env_response_t *r, const char *fmt, ...); + ZEND_RESULT_CODE (*del_header)(php_http_env_response_t *r, const char *header_str, size_t header_len); + ZEND_RESULT_CODE (*write)(php_http_env_response_t *r, const char *data_str, size_t data_len); + ZEND_RESULT_CODE (*flush)(php_http_env_response_t *r); + ZEND_RESULT_CODE (*finish)(php_http_env_response_t *r); + } php_http_env_response_ops_t; + + PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void); + PHP_HTTP_API php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void); + + struct php_http_env_response { + void *ctx; + php_http_env_response_ops_t *ops; + + php_http_cookie_list_t *cookies; + php_http_buffer_t *buffer; - zval *options; ++ zval options; + + struct { + size_t chunk; + double delay; + } throttle; + + struct { + php_http_range_status_t status; + HashTable values; + char boundary[32]; + } range; + + struct { + size_t length; + char *type; + char *encoding; + + php_http_encoding_stream_t *encoder; + } content; + + zend_bool done; - -#ifdef ZTS - void ***ts; -#endif + }; + -PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *ops_ctx TSRMLS_DC); ++PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *ops_ctx); + PHP_HTTP_API ZEND_RESULT_CODE php_http_env_response_send(php_http_env_response_t *r); + PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r); + PHP_HTTP_API void php_http_env_response_free(php_http_env_response_t **r); + -PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC); -PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC); ++PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request); ++PHP_HTTP_API php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request); + + PHP_HTTP_API zend_class_entry *php_http_env_response_class_entry; + PHP_MINIT_FUNCTION(http_env_response); + + #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 --cc src/php_http_etag.c index 0000000,3604ad8..1ebddb3 mode 000000,100644..100644 --- a/src/php_http_etag.c +++ b/src/php_http_etag.c @@@ -1,0 -1,131 +1,130 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #ifdef PHP_HTTP_HAVE_HASH + # include "php_hash.h" + #endif + + #include + #include + #include + -php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC) ++php_http_etag_t *php_http_etag_init(const char *mode) + { + void *ctx; + php_http_etag_t *e; + + if (mode && (!strcasecmp(mode, "crc32b"))) { + ctx = emalloc(sizeof(uint)); + *((uint *) ctx) = ~0; + } else if (mode && !strcasecmp(mode, "sha1")) { + PHP_SHA1Init(ctx = emalloc(sizeof(PHP_SHA1_CTX))); + } else if (mode && !strcasecmp(mode, "md5")) { + PHP_MD5Init(ctx = emalloc(sizeof(PHP_MD5_CTX))); + } else { + #ifdef PHP_HTTP_HAVE_HASH + const php_hash_ops *eho = NULL; + + if (mode && (eho = php_hash_fetch_ops(mode, strlen(mode)))) { + ctx = emalloc(eho->context_size); + eho->hash_init(ctx); + } else + #endif + return NULL; + } + + e = emalloc(sizeof(*e)); + e->ctx = ctx; + e->mode = estrdup(mode); - TSRMLS_SET_CTX(e->ts); + + return e; + } + + char *php_http_etag_finish(php_http_etag_t *e) + { + unsigned char digest[128] = {0}; + char *etag = NULL; + + if (!strcasecmp(e->mode, "crc32b")) { + unsigned char buf[4]; + + *((uint *) e->ctx) = ~*((uint *) e->ctx); + #ifdef WORDS_BIGENDIAN + etag = php_http_etag_digest((unsigned char *) e->ctx, 4); + #else + buf[0] = ((unsigned char *) e->ctx)[3]; + buf[1] = ((unsigned char *) e->ctx)[2]; + buf[2] = ((unsigned char *) e->ctx)[1]; + buf[3] = ((unsigned char *) e->ctx)[0]; + etag = php_http_etag_digest(buf, 4); + #endif + } else if ((!strcasecmp(e->mode, "sha1"))) { + PHP_SHA1Final(digest, e->ctx); + etag = php_http_etag_digest(digest, 20); + } else if ((!strcasecmp(e->mode, "md5"))) { + PHP_MD5Final(digest, e->ctx); + etag = php_http_etag_digest(digest, 16); + } else { + #ifdef PHP_HTTP_HAVE_HASH + const php_hash_ops *eho = NULL; + + if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) { + eho->hash_final(digest, e->ctx); + etag = php_http_etag_digest(digest, eho->digest_size); + } + #endif + } + + efree(e->ctx); + efree(e->mode); + efree(e); + + return etag; + } + + size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len) + { + if (!strcasecmp(e->mode, "crc32b")) { + uint i, c = *((uint *) e->ctx); + for (i = 0; i < data_len; ++i) { + CRC32(c, data_ptr[i]); + } + *((uint *) e->ctx) = c; + } else if ((!strcasecmp(e->mode, "sha1"))) { + PHP_SHA1Update(e->ctx, (const unsigned char *) data_ptr, data_len); + } else if ((!strcasecmp(e->mode, "md5"))) { + PHP_MD5Update(e->ctx, (const unsigned char *) data_ptr, data_len); + } else { + #ifdef PHP_HTTP_HAVE_HASH + const php_hash_ops *eho = NULL; + + if (e->mode && (eho = php_hash_fetch_ops(e->mode, strlen(e->mode)))) { + eho->hash_update(e->ctx, (const unsigned char *) data_ptr, data_len); + } + #endif + } + + return data_len; + } + + + /* + * 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 --cc src/php_http_etag.h index 0000000,bf6cf49..f208db7 mode 000000,100644..100644 --- a/src/php_http_etag.h +++ b/src/php_http_etag.h @@@ -1,0 -1,55 +1,51 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_ETAG_H + #define PHP_HTTP_ETAG_H + + typedef struct php_http_etag { + void *ctx; + char *mode; - -#ifdef ZTS - void ***ts; -#endif + } php_http_etag_t; + -PHP_HTTP_API php_http_etag_t *php_http_etag_init(const char *mode TSRMLS_DC); ++PHP_HTTP_API php_http_etag_t *php_http_etag_init(const char *mode); + PHP_HTTP_API size_t php_http_etag_update(php_http_etag_t *e, const char *data_ptr, size_t data_len); + PHP_HTTP_API char *php_http_etag_finish(php_http_etag_t *e); + + static inline char *php_http_etag_digest(const unsigned char *digest, int len) + { + static const char hexdigits[17] = "0123456789abcdef"; + int i; + char *hex = emalloc(len * 2 + 1); + char *ptr = hex; + + for (i = 0; i < len; ++i) { + *ptr++ = hexdigits[digest[i] >> 4]; + *ptr++ = hexdigits[digest[i] & 0xF]; + } + *ptr = '\0'; + + return hex; + } + + #endif /* PHP_HTTP_ETAG_H */ + + /* + * 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 --cc src/php_http_exception.c index 0000000,25a33e6..bae09f9 mode 000000,100644..100644 --- a/src/php_http_exception.c +++ b/src/php_http_exception.c @@@ -1,0 -1,125 +1,125 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + + #ifndef PHP_HTTP_DBG_EXCEPTIONS + # define PHP_HTTP_DBG_EXCEPTIONS 0 + #endif + + #if PHP_HTTP_DBG_EXCEPTIONS -static void php_http_exception_hook(zval *ex TSRMLS_DC) ++static void php_http_exception_hook(zval *ex) + { + if (ex) { - zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC); ++ zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0); + fprintf(stderr, "*** Threw exception '%s'\n", Z_STRVAL_P(m)); + } else { + fprintf(stderr, "*** Threw NULL exception\n"); + } + } + #endif + + zend_class_entry *php_http_exception_interface_class_entry; + zend_class_entry *php_http_exception_runtime_class_entry; + zend_class_entry *php_http_exception_unexpected_val_class_entry; + zend_class_entry *php_http_exception_bad_method_call_class_entry; + zend_class_entry *php_http_exception_invalid_arg_class_entry; + zend_class_entry *php_http_exception_bad_header_class_entry; + zend_class_entry *php_http_exception_bad_url_class_entry; + zend_class_entry *php_http_exception_bad_message_class_entry; + zend_class_entry *php_http_exception_bad_conversion_class_entry; + zend_class_entry *php_http_exception_bad_querystring_class_entry; + + PHP_MINIT_FUNCTION(http_exception) + { + zend_class_entry *cep, ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Exception", NULL); - php_http_exception_interface_class_entry = zend_register_internal_interface(&ce TSRMLS_CC); ++ php_http_exception_interface_class_entry = zend_register_internal_interface(&ce); + + /* + * Would be great to only have a few exceptions and rather more identifying + * error codes, but zend_replace_error_handling() does not accept any codes. + */ + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "RuntimeException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_runtime_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "UnexpectedValueException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_UnexpectedValueException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_UnexpectedValueException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_unexpected_val_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMethodCallException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_method_call_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "InvalidArgumentException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_invalid_arg_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadHeaderException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_header_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadUrlException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_url_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadMessageException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_message_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadConversionException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_conversion_class_entry = cep; + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "http\\Exception", "BadQueryStringException", NULL); - cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException, NULL TSRMLS_CC); - zend_class_implements(cep TSRMLS_CC, 1, php_http_exception_interface_class_entry); ++ cep = zend_register_internal_class_ex(&ce, spl_ce_DomainException); ++ zend_class_implements(cep, 1, php_http_exception_interface_class_entry); + php_http_exception_bad_querystring_class_entry = cep; + + #if PHP_HTTP_DBG_EXCEPTIONS + zend_throw_exception_hook = php_http_exception_hook; + #endif + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_exception.h index 0000000,969a351..eaf7a13 mode 000000,100644..100644 --- a/src/php_http_exception.h +++ b/src/php_http_exception.h @@@ -1,0 -1,55 +1,55 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_EXCEPTION_H + #define PHP_HTTP_EXCEPTION_H + + /* short hand for zend_throw_exception_ex */ + #define php_http_throw(e, fmt, ...) \ - zend_throw_exception_ex(php_http_exception_ ##e## _class_entry, 0 TSRMLS_CC, fmt, __VA_ARGS__) ++ zend_throw_exception_ex(php_http_exception_ ##e## _class_entry, 0, fmt, __VA_ARGS__) + + /* wrap a call with replaced zend_error_handling */ + #define php_http_expect(test, e, fail) \ + do { \ + zend_error_handling __zeh; \ - zend_replace_error_handling(EH_THROW, php_http_exception_ ##e## _class_entry, &__zeh TSRMLS_CC); \ ++ zend_replace_error_handling(EH_THROW, php_http_exception_ ##e## _class_entry, &__zeh); \ + if (!(test)) { \ - zend_restore_error_handling(&__zeh TSRMLS_CC); \ ++ zend_restore_error_handling(&__zeh); \ + fail; \ + } \ - zend_restore_error_handling(&__zeh TSRMLS_CC); \ ++ zend_restore_error_handling(&__zeh); \ + } while(0) + + PHP_HTTP_API zend_class_entry *php_http_exception_interface_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_runtime_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_unexpected_val_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_method_call_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_invalid_arg_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_header_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_url_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_message_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_conversion_class_entry; + PHP_HTTP_API zend_class_entry *php_http_exception_bad_querystring_class_entry; + + PHP_MINIT_FUNCTION(http_exception); + + #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 --cc src/php_http_filter.c index 0000000,b6d967b..f145f02 mode 000000,100644..100644 --- a/src/php_http_filter.c +++ b/src/php_http_filter.c @@@ -1,0 -1,453 +1,447 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #ifndef DBG_FILTER + # define DBG_FILTER 0 + #endif + + PHP_MINIT_FUNCTION(http_filter) + { - php_stream_filter_register_factory("http.*", &php_http_filter_factory TSRMLS_CC); ++ php_stream_filter_register_factory("http.*", &php_http_filter_factory); + return SUCCESS; + } + + #define PHP_HTTP_FILTER_PARAMS \ + php_stream *stream, \ + php_stream_filter *this, \ + php_stream_bucket_brigade *buckets_in, \ + php_stream_bucket_brigade *buckets_out, \ - size_t *bytes_consumed, int flags \ - TSRMLS_DC ++ size_t *bytes_consumed, \ ++ int flags + #define PHP_HTTP_FILTER_OP(filter) \ + http_filter_op_ ##filter + #define PHP_HTTP_FILTER_OPS(filter) \ + php_stream_filter_ops PHP_HTTP_FILTER_OP(filter) + #define PHP_HTTP_FILTER_DTOR(filter) \ + http_filter_ ##filter## _dtor + #define PHP_HTTP_FILTER_DESTRUCTOR(filter) \ - void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this TSRMLS_DC) ++ void PHP_HTTP_FILTER_DTOR(filter)(php_stream_filter *this) + #define PHP_HTTP_FILTER_FUNC(filter) \ + http_filter_ ##filter + #define PHP_HTTP_FILTER_FUNCTION(filter) \ + php_stream_filter_status_t PHP_HTTP_FILTER_FUNC(filter)(PHP_HTTP_FILTER_PARAMS) + #define PHP_HTTP_FILTER_BUFFER(filter) \ + http_filter_ ##filter## _buffer + + #define PHP_HTTP_FILTER_IS_CLOSING(stream, flags) \ + ( (flags & PSFS_FLAG_FLUSH_CLOSE) \ + || php_stream_eof(stream) \ + || ((stream->ops == &php_stream_temp_ops || stream->ops == &php_stream_memory_ops) && stream->eof) \ + ) + + #define NEW_BUCKET(data, length) \ + { \ + char *__data; \ + php_stream_bucket *__buck; \ + \ + __data = pemalloc(length, this->is_persistent); \ + if (!__data) { \ + return PSFS_ERR_FATAL; \ + } \ + memcpy(__data, data, length); \ + \ - __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent TSRMLS_CC); \ ++ __buck = php_stream_bucket_new(stream, __data, length, 1, this->is_persistent); \ + if (!__buck) { \ + pefree(__data, this->is_persistent); \ + return PSFS_ERR_FATAL; \ + } \ + \ - php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \ ++ php_stream_bucket_append(buckets_out, __buck); \ + } + + typedef struct _http_chunked_decode_filter_buffer_t { + php_http_buffer_t buffer; + ulong hexlen; + } PHP_HTTP_FILTER_BUFFER(chunked_decode); + + typedef php_http_encoding_stream_t PHP_HTTP_FILTER_BUFFER(zlib); + + static PHP_HTTP_FILTER_FUNCTION(chunked_decode) + { + int out_avail = 0; + php_stream_bucket *ptr, *nxt; - PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); ++ PHP_HTTP_FILTER_BUFFER(chunked_decode) *buffer = Z_PTR(this->abstract); + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(PHP_HTTP_BUFFER(buffer), ptr->buf, ptr->buflen)) { + return PSFS_ERR_FATAL; + } + + nxt = ptr->next; - php_stream_bucket_unlink(ptr TSRMLS_CC); - php_stream_bucket_delref(ptr TSRMLS_CC); ++ php_stream_bucket_unlink(ptr); ++ php_stream_bucket_delref(ptr); + } + + if (!php_http_buffer_fix(PHP_HTTP_BUFFER(buffer))) { + return PSFS_ERR_FATAL; + } + + /* we have data in our buffer */ + while (PHP_HTTP_BUFFER(buffer)->used) { + + /* we already know the size of the chunk and are waiting for data */ + if (buffer->hexlen) { + + /* not enough data buffered */ + if (PHP_HTTP_BUFFER(buffer)->used < buffer->hexlen) { + + /* flush anyway? */ + if (flags & PSFS_FLAG_FLUSH_INC) { + + /* flush all data (should only be chunk data) */ + out_avail = 1; + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used); + + /* waiting for less data now */ + buffer->hexlen -= PHP_HTTP_BUFFER(buffer)->used; + /* no more buffered data */ + php_http_buffer_reset(PHP_HTTP_BUFFER(buffer)); + /* break */ + } + + /* we have too less data and don't need to flush */ + else { + break; + } + } + + /* we seem to have all data of the chunk */ + else { + out_avail = 1; + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, buffer->hexlen); + + /* remove outgoing data from the buffer */ + php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, buffer->hexlen); + /* reset hexlen */ + buffer->hexlen = 0; + /* continue */ + } + } + + /* we don't know the length of the chunk yet */ + else { + size_t off = 0; + + /* ignore preceeding CRLFs (too loose?) */ + while (off < PHP_HTTP_BUFFER(buffer)->used && ( + PHP_HTTP_BUFFER(buffer)->data[off] == '\n' || + PHP_HTTP_BUFFER(buffer)->data[off] == '\r')) { + ++off; + } + if (off) { + php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, off); + } + + /* still data there? */ + if (PHP_HTTP_BUFFER(buffer)->used) { + int eollen; + const char *eolstr; + + /* we need eol, so we can be sure we have all hex digits */ + php_http_buffer_fix(PHP_HTTP_BUFFER(buffer)); + if ((eolstr = php_http_locate_bin_eol(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used, &eollen))) { + char *stop = NULL; + + /* read in chunk size */ + buffer->hexlen = strtoul(PHP_HTTP_BUFFER(buffer)->data, &stop, 16); + + /* if strtoul() stops at the beginning of the buffered data + there's something oddly wrong, i.e. bad input */ + if (stop == PHP_HTTP_BUFFER(buffer)->data) { + return PSFS_ERR_FATAL; + } + + /* cut out */ + php_http_buffer_cut(PHP_HTTP_BUFFER(buffer), 0, eolstr + eollen - PHP_HTTP_BUFFER(buffer)->data); + /* buffer->hexlen is 0 now or contains the size of the next chunk */ + if (!buffer->hexlen) { - php_stream_notify_info(stream->context, PHP_STREAM_NOTIFY_COMPLETED, NULL, 0); ++ php_stream_notify_info(PHP_STREAM_CONTEXT(stream), PHP_STREAM_NOTIFY_COMPLETED, NULL, 0); + break; + } + /* continue */ + } else { + /* we have not enough data buffered to read in chunk size */ + break; + } + } + /* break */ + } + } + + /* flush before close, but only if we are already waiting for more data */ + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags) && buffer->hexlen && PHP_HTTP_BUFFER(buffer)->used) { + out_avail = 1; + NEW_BUCKET(PHP_HTTP_BUFFER(buffer)->data, PHP_HTTP_BUFFER(buffer)->used); + php_http_buffer_reset(PHP_HTTP_BUFFER(buffer)); + buffer->hexlen = 0; + } + + return out_avail ? PSFS_PASS_ON : PSFS_FEED_ME; + } + + static PHP_HTTP_FILTER_DESTRUCTOR(chunked_decode) + { - PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = (PHP_HTTP_FILTER_BUFFER(chunked_decode) *) (this->abstract); ++ PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = Z_PTR(this->abstract); + + php_http_buffer_dtor(PHP_HTTP_BUFFER(b)); + pefree(b, this->is_persistent); + } + + static PHP_HTTP_FILTER_FUNCTION(chunked_encode) + { + php_http_buffer_t buf; + php_stream_bucket *ptr, *nxt; + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* new data available? */ + php_http_buffer_init(&buf); + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + #if DBG_FILTER + fprintf(stderr, "update: chunked (-> %zu) (w: %zu, r: %zu)\n", ptr->buflen, stream->writepos, stream->readpos); + #endif + + nxt = ptr->next; - php_stream_bucket_unlink(ptr TSRMLS_CC); ++ php_stream_bucket_unlink(ptr); + php_http_buffer_appendf(&buf, "%lx" PHP_HTTP_CRLF, (long unsigned int) ptr->buflen); + php_http_buffer_append(&buf, ptr->buf, ptr->buflen); + php_http_buffer_appends(&buf, PHP_HTTP_CRLF); + + /* pass through */ + NEW_BUCKET(buf.data, buf.used); + /* reset */ + php_http_buffer_reset(&buf); - php_stream_bucket_delref(ptr TSRMLS_CC); ++ php_stream_bucket_delref(ptr); + } + + /* free buffer */ + php_http_buffer_dtor(&buf); + + /* terminate with "0" */ + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) { + #if DBG_FILTER + fprintf(stderr, "finish: chunked\n"); + #endif + + NEW_BUCKET("0" PHP_HTTP_CRLF PHP_HTTP_CRLF, lenof("0" PHP_HTTP_CRLF PHP_HTTP_CRLF)); + } + + return PSFS_PASS_ON; + } + + static PHP_HTTP_FILTER_OPS(chunked_decode) = { + PHP_HTTP_FILTER_FUNC(chunked_decode), + PHP_HTTP_FILTER_DTOR(chunked_decode), + "http.chunked_decode" + }; + + static PHP_HTTP_FILTER_OPS(chunked_encode) = { + PHP_HTTP_FILTER_FUNC(chunked_encode), + NULL, + "http.chunked_encode" + }; + + static PHP_HTTP_FILTER_FUNCTION(zlib) + { + php_stream_bucket *ptr, *nxt; - PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract; ++ PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract); + + if (bytes_consumed) { + *bytes_consumed = 0; + } + + /* fetch available bucket data */ + for (ptr = buckets_in->head; ptr; ptr = nxt) { + char *encoded = NULL; + size_t encoded_len = 0; + + if (bytes_consumed) { + *bytes_consumed += ptr->buflen; + } + + #if DBG_FILTER + fprintf(stderr, "bucket: b=%p p=%p p=%p\n", ptr->brigade, ptr->prev, ptr->next); + #endif + + nxt = ptr->next; - php_stream_bucket_unlink(ptr TSRMLS_CC); ++ php_stream_bucket_unlink(ptr); + php_http_encoding_stream_update(buffer, ptr->buf, ptr->buflen, &encoded, &encoded_len); + + #if DBG_FILTER + fprintf(stderr, "update: deflate (-> %zu) (w: %zu, r: %zu)\n", encoded_len, stream->writepos, stream->readpos); + #endif + + if (encoded) { + if (encoded_len) { + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } - php_stream_bucket_delref(ptr TSRMLS_CC); ++ php_stream_bucket_delref(ptr); + } + + /* flush & close */ + if (flags & PSFS_FLAG_FLUSH_INC) { + char *encoded = NULL; + size_t encoded_len = 0; + + php_http_encoding_stream_flush(buffer, &encoded, &encoded_len); + + #if DBG_FILTER + fprintf(stderr, "flush: deflate (-> %zu)\n", encoded_len); + #endif + + if (encoded) { + if (encoded_len) { + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + if (PHP_HTTP_FILTER_IS_CLOSING(stream, flags)) { + char *encoded = NULL; + size_t encoded_len = 0; + + php_http_encoding_stream_finish(buffer, &encoded, &encoded_len); + + #if DBG_FILTER + fprintf(stderr, "finish: deflate (-> %zu)\n", encoded_len); + #endif + + if (encoded) { + if (encoded_len) { + NEW_BUCKET(encoded, encoded_len); + } + efree(encoded); + } + } + + return PSFS_PASS_ON; + } + static PHP_HTTP_FILTER_DESTRUCTOR(zlib) + { - PHP_HTTP_FILTER_BUFFER(zlib) *buffer = (PHP_HTTP_FILTER_BUFFER(zlib) *) this->abstract; ++ PHP_HTTP_FILTER_BUFFER(zlib) *buffer = Z_PTR(this->abstract); + php_http_encoding_stream_free(&buffer); + } + + static PHP_HTTP_FILTER_OPS(deflate) = { + PHP_HTTP_FILTER_FUNC(zlib), + PHP_HTTP_FILTER_DTOR(zlib), + "http.deflate" + }; + + static PHP_HTTP_FILTER_OPS(inflate) = { + PHP_HTTP_FILTER_FUNC(zlib), + PHP_HTTP_FILTER_DTOR(zlib), + "http.inflate" + }; + -static php_stream_filter *http_filter_create(const char *name, zval *params, int p TSRMLS_DC) ++static php_stream_filter *http_filter_create(const char *name, zval *params, int p) + { - zval **tmp = ¶ms; ++ zval *tmp = params; + php_stream_filter *f = NULL; + int flags = p ? PHP_HTTP_ENCODING_STREAM_PERSISTENT : 0; + + if (params) { + switch (Z_TYPE_P(params)) { - case IS_ARRAY: - case IS_OBJECT: - if (SUCCESS != zend_hash_find(HASH_OF(params), "flags", sizeof("flags"), (void *) &tmp)) { - break; - } - /* no break */ - default: - { - zval *num = php_http_ztyp(IS_LONG, *tmp); - - flags |= (Z_LVAL_P(num) & 0x0fffffff); - zval_ptr_dtor(&num); - ++ case IS_ARRAY: ++ case IS_OBJECT: ++ if (!(tmp = zend_hash_str_find_ind(HASH_OF(params), ZEND_STRL("flags")))) { ++ break; + } ++ /* no break */ ++ default: ++ flags |= zval_get_long(tmp) & 0x0fffffff; + break; + } + } + + if (!strcasecmp(name, "http.chunked_decode")) { + PHP_HTTP_FILTER_BUFFER(chunked_decode) *b = NULL; + + if ((b = pecalloc(1, sizeof(PHP_HTTP_FILTER_BUFFER(chunked_decode)), p))) { + php_http_buffer_init_ex(PHP_HTTP_BUFFER(b), 4096, p ? PHP_HTTP_BUFFER_INIT_PERSISTENT : 0); + if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_decode), b, p))) { + pefree(b, p); + } + } + } else + + if (!strcasecmp(name, "http.chunked_encode")) { + f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(chunked_encode), NULL, p); + } else + + if (!strcasecmp(name, "http.inflate")) { + PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL; + - if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags TSRMLS_CC))) { ++ if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), flags))) { + if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(inflate), b, p))) { + php_http_encoding_stream_free(&b); + } + } + } else + + if (!strcasecmp(name, "http.deflate")) { + PHP_HTTP_FILTER_BUFFER(zlib) *b = NULL; + - if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags TSRMLS_CC))) { ++ if ((b = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), flags))) { + if (!(f = php_stream_filter_alloc(&PHP_HTTP_FILTER_OP(deflate), b, p))) { + php_http_encoding_stream_free(&b); + } + } + } + + return f; + } + + php_stream_filter_factory php_http_filter_factory = { + http_filter_create + }; + + + /* + * 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 --cc src/php_http_header.c index 0000000,41601df..d4f2b51 mode 000000,100644..100644 --- a/src/php_http_header.c +++ b/src/php_http_header.c @@@ -1,0 -1,383 +1,387 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC) ++ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data) + { + php_http_header_parser_t ctx; + php_http_buffer_t buf; + php_http_header_parser_state_t rs; + + if (!php_http_buffer_from_string_ex(&buf, header, length)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not allocate buffer"); ++ php_error_docref(NULL, E_WARNING, "Could not allocate buffer"); + return FAILURE; + } + - if (!php_http_header_parser_init(&ctx TSRMLS_CC)) { ++ if (!php_http_header_parser_init(&ctx)) { + php_http_buffer_dtor(&buf); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not initialize header parser"); ++ php_error_docref(NULL, E_WARNING, "Could not initialize header parser"); + return FAILURE; + } + + rs = php_http_header_parser_parse(&ctx, &buf, PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_data); + php_http_header_parser_dtor(&ctx); + php_http_buffer_dtor(&buf); + + return rs == PHP_HTTP_HEADER_PARSER_STATE_FAILURE ? FAILURE : SUCCESS; + } + -void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC) ++void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg) + { - HashPosition pos1, pos2; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **header, **single_header; - - FOREACH_HASH_KEYVAL(pos1, headers, key, header) { - if (key.type == HASH_KEY_IS_STRING) { - if (key.len == sizeof("Set-Cookie") && !strcasecmp(key.str, "Set-Cookie") && Z_TYPE_PP(header) == IS_ARRAY) { - FOREACH_VAL(pos2, *header, single_header) { - if (Z_TYPE_PP(single_header) == IS_ARRAY) { - php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, *single_header TSRMLS_CC); ++ php_http_arrkey_t key; ++ zval *header, *single_header; ++ ++ ZEND_HASH_FOREACH_KEY_VAL(headers, key.h, key.key, header) ++ { ++ if (key.key) { ++ if (zend_string_equals_literal(key.key, "Set-Cookie") && Z_TYPE_P(header) == IS_ARRAY) { ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), single_header) ++ { ++ if (Z_TYPE_P(single_header) == IS_ARRAY) { ++ php_http_cookie_list_t *cookie = php_http_cookie_list_from_struct(NULL, single_header); + + if (cookie) { + char *buf; + size_t len; + + php_http_cookie_list_to_string(cookie, &buf, &len); + cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", buf); + php_http_cookie_list_free(&cookie); + efree(buf); + } + } else { - zval *strval = php_http_header_value_to_string(*single_header TSRMLS_CC); ++ zend_string *zs = php_http_header_value_to_string(single_header); + - cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval)); - zval_ptr_dtor(&strval); ++ cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", zs->val); ++ zend_string_release(zs); + } + } ++ ZEND_HASH_FOREACH_END(); + } else { - zval *strval = php_http_header_value_to_string(*header TSRMLS_CC); ++ zend_string *zs = php_http_header_value_to_string(header); + - cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval)); - zval_ptr_dtor(&strval); ++ cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.key->val, zs->val); ++ zend_string_release(zs); + } + } + } ++ ZEND_HASH_FOREACH_END(); + } + -void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC) ++void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers) + { - php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC); ++ php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str); + } + -zval *php_http_header_value_to_string(zval *header TSRMLS_DC) ++zend_string *php_http_header_value_array_to_string(zval *header) + { - zval *ret; - - if (Z_TYPE_P(header) == IS_BOOL) { - MAKE_STD_ZVAL(ret); - ZVAL_STRING(ret, Z_BVAL_P(header) ? "true" : "false", 1); - } else if (Z_TYPE_P(header) == IS_ARRAY) { - zval **val; - HashPosition pos; - php_http_buffer_t str; - - php_http_buffer_init(&str); - MAKE_STD_ZVAL(ret); - FOREACH_VAL(pos,header, val) { - zval *strval = php_http_header_value_to_string(*val TSRMLS_CC); - - php_http_buffer_appendf(&str, str.used ? ", %s":"%s", Z_STRVAL_P(strval)); - zval_ptr_dtor(&strval); - } - php_http_buffer_fix(&str); - ZVAL_STRINGL(ret, str.data, str.used, 0); - } else { - ret = php_http_zsep(1, IS_STRING, header); ++ zval *val; ++ php_http_buffer_t str; ++ ++ php_http_buffer_init(&str); ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(header), val) ++ { ++ zend_string *zs = php_http_header_value_to_string(val); ++ ++ php_http_buffer_appendf(&str, str.used ? ", %s":"%s", zs->val); ++ zend_string_release(zs); + } ++ ZEND_HASH_FOREACH_END(); ++ php_http_buffer_fix(&str); + - return ret; ++ return php_http_cs2zs(str.data, str.used); ++} ++ ++zend_string *php_http_header_value_to_string(zval *header) ++{ ++ switch (Z_TYPE_P(header)) { ++ case IS_TRUE: ++ return zend_string_init(ZEND_STRL("true"), 0); ++ case IS_FALSE: ++ return zend_string_init(ZEND_STRL("false"), 0); ++ case IS_ARRAY: ++ return php_http_header_value_array_to_string(header); ++ default: ++ return zval_get_string(header); ++ } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader___construct, 0, 0, 0) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, __construct) + { + char *name_str = NULL, *value_str = NULL; - int name_len = 0, value_len = 0; ++ size_t name_len = 0, value_len = 0; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!", &name_str, &name_len, &value_str, &value_len), invalid_arg, return); + + if (name_str && name_len) { + char *pretty_str = estrndup(name_str, name_len); - zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), php_http_pretty_key(pretty_str, name_len, 1, 1), name_len TSRMLS_CC); ++ zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), php_http_pretty_key(pretty_str, name_len, 1, 1), name_len); + efree(pretty_str); + } + if (value_str && value_len) { - zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("value"), value_str, value_len TSRMLS_CC); ++ zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("value"), value_str, value_len); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_serialize, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, serialize) + { + if (SUCCESS == zend_parse_parameters_none()) { + php_http_buffer_t buf; - zval *zname, *zvalue; ++ zend_string *zs; ++ zval name_tmp, value_tmp; + + php_http_buffer_init(&buf); - zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC)); - php_http_buffer_append(&buf, Z_STRVAL_P(zname), Z_STRLEN_P(zname)); - zval_ptr_dtor(&zname); - zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC)); - if (Z_STRLEN_P(zvalue)) { ++ zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0, &name_tmp)); ++ php_http_buffer_appendz(&buf, zs); ++ zend_string_release(zs); ++ ++ zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp)); ++ if (zs->len) { + php_http_buffer_appends(&buf, ": "); - php_http_buffer_append(&buf, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); ++ php_http_buffer_appendz(&buf, zs); + } else { + php_http_buffer_appends(&buf, ":"); + } - zval_ptr_dtor(&zvalue); ++ zend_string_release(zs); + - RETURN_PHP_HTTP_BUFFER_VAL(&buf); ++ RETURN_STR(php_http_cs2zs(buf.data, buf.used)); + } + RETURN_EMPTY_STRING(); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_unserialize, 0, 0, 1) + ZEND_ARG_INFO(0, serialized) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, unserialize) + { + char *serialized_str; - int serialized_len; ++ size_t serialized_len; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized_str, &serialized_len)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &serialized_str, &serialized_len)) { + HashTable ht; + + zend_hash_init(&ht, 1, NULL, ZVAL_PTR_DTOR, 0); - if (SUCCESS == php_http_header_parse(serialized_str, serialized_len, &ht, NULL, NULL TSRMLS_CC)) { ++ if (SUCCESS == php_http_header_parse(serialized_str, serialized_len, &ht, NULL, NULL)) { + if (zend_hash_num_elements(&ht)) { - zval **val, *cpy; - char *str; - uint len; - ulong idx; ++ zend_string *zs, *key; ++ zend_ulong idx; + + zend_hash_internal_pointer_reset(&ht); - switch (zend_hash_get_current_key_ex(&ht, &str, &len, &idx, 0, NULL)) { ++ switch (zend_hash_get_current_key(&ht, &key, &idx)) { + case HASH_KEY_IS_STRING: - zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), str, len - 1 TSRMLS_CC); ++ zend_update_property_str(php_http_header_class_entry, getThis(), ZEND_STRL("name"), key); + break; + case HASH_KEY_IS_LONG: - zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx TSRMLS_CC); ++ zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx); + break; + default: + break; + } - zend_hash_get_current_data(&ht, (void *) &val); - cpy = php_http_zsep(1, IS_STRING, *val); - zend_update_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), cpy TSRMLS_CC); - zval_ptr_dtor(&cpy); ++ zs = zval_get_string(zend_hash_get_current_data(&ht)); ++ zend_update_property_str(php_http_header_class_entry, getThis(), ZEND_STRL("value"), zs); ++ zend_string_release(zs); + } + } + zend_hash_destroy(&ht); + } + + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_match, 0, 0, 1) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, match) + { + char *val_str; - int val_len; - long flags = PHP_HTTP_MATCH_LOOSE; - zval *zvalue; ++ size_t val_len; ++ zend_long flags = PHP_HTTP_MATCH_LOOSE; ++ zend_string *zs; ++ zval value_tmp; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &val_str, &val_len, &flags)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "|sl", &val_str, &val_len, &flags)) { + return; + } + - zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC)); - RETVAL_BOOL(php_http_match(Z_STRVAL_P(zvalue), val_str, flags)); - zval_ptr_dtor(&zvalue); ++ zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp)); ++ RETVAL_BOOL(php_http_match(zs->val, val_str, flags)); ++ zend_string_release(zs); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_negotiate, 0, 0, 1) + ZEND_ARG_INFO(0, supported) + ZEND_ARG_INFO(1, result) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, negotiate) + { + HashTable *supported, *rs; - zval *zname, *zvalue, *rs_array = NULL; ++ zval name_tmp, value_tmp, *rs_array = NULL; ++ zend_string *zs; + char *sep_str = NULL; + size_t sep_len = 0; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "H|z", &supported, &rs_array)) { + return; + } + if (rs_array) { ++ ZVAL_DEREF(rs_array); + zval_dtor(rs_array); + array_init(rs_array); + } + - zname = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0 TSRMLS_CC)); - if (!strcasecmp(Z_STRVAL_P(zname), "Accept")) { ++ zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("name"), 0, &name_tmp)); ++ if (zend_string_equals_literal(zs, "Accept")) { + sep_str = "/"; + sep_len = 1; - } else if (!strcasecmp(Z_STRVAL_P(zname), "Accept-Language")) { ++ } else if (zend_string_equals_literal(zs, "Accept-Language")) { + sep_str = "-"; + sep_len = 1; + } - zval_ptr_dtor(&zname); ++ zend_string_release(zs); + - zvalue = php_http_ztyp(IS_STRING, zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0 TSRMLS_CC)); - if ((rs = php_http_negotiate(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), supported, sep_str, sep_len TSRMLS_CC))) { ++ zs = zval_get_string(zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp)); ++ if ((rs = php_http_negotiate(zs->val, zs->len, supported, sep_str, sep_len))) { + PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(rs, supported, rs_array); + } else { + PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); + } - zval_ptr_dtor(&zvalue); ++ zend_string_release(zs); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_getParams, 0, 0, 0) + ZEND_ARG_INFO(0, param_sep) + ZEND_ARG_INFO(0, arg_sep) + ZEND_ARG_INFO(0, val_sep) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, getParams) + { - zval zctor, *zparams_obj, **zargs = NULL; ++ zval value_tmp, zctor, zparams_obj, *zargs = NULL; + - INIT_PZVAL(&zctor); - ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"), 0); ++ ZVAL_STRINGL(&zctor, "__construct", lenof("__construct")); + - MAKE_STD_ZVAL(zparams_obj); - object_init_ex(zparams_obj, php_http_params_class_entry); ++ object_init_ex(&zparams_obj, php_http_params_class_entry); + - zargs = (zval **) ecalloc(ZEND_NUM_ARGS()+1, sizeof(zval *)); - zargs[0] = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("value"), 0 TSRMLS_CC); ++ zargs = (zval *) ecalloc(ZEND_NUM_ARGS()+1, sizeof(zval)); ++ ZVAL_COPY_VALUE(&zargs[0], zend_read_property(php_http_header_class_entry, getThis(), ZEND_STRL("value"), 0, &value_tmp)); + if (ZEND_NUM_ARGS()) { + zend_get_parameters_array(ZEND_NUM_ARGS(), ZEND_NUM_ARGS(), &zargs[1]); + } + - if (SUCCESS == call_user_function(NULL, &zparams_obj, &zctor, return_value, ZEND_NUM_ARGS()+1, zargs TSRMLS_CC)) { - RETVAL_ZVAL(zparams_obj, 0, 1); ++ if (SUCCESS == call_user_function(NULL, &zparams_obj, &zctor, return_value, ZEND_NUM_ARGS()+1, zargs)) { ++ RETVAL_ZVAL(&zparams_obj, 0, 1); + } + ++ zval_ptr_dtor(&zctor); + if (zargs) { + efree(zargs); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeader_parse, 0, 0, 1) + ZEND_ARG_INFO(0, string) + ZEND_ARG_INFO(0, header_class) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpHeader, parse) + { + char *header_str; - int header_len; ++ size_t header_len; + zend_class_entry *ce = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C", &header_str, &header_len, &ce)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|C", &header_str, &header_len, &ce)) { + array_init(return_value); + - if (SUCCESS != php_http_header_parse(header_str, header_len, Z_ARRVAL_P(return_value), NULL, NULL TSRMLS_CC)) { ++ if (SUCCESS != php_http_header_parse(header_str, header_len, Z_ARRVAL_P(return_value), NULL, NULL)) { + zval_dtor(return_value); + RETURN_FALSE; + } else { - if (ce && instanceof_function(ce, php_http_header_class_entry TSRMLS_CC)) { - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **val; - - FOREACH_KEYVAL(pos, return_value, key, val) { - zval *zho, *zkey, *zvalue; ++ if (ce && instanceof_function(ce, php_http_header_class_entry)) { ++ php_http_arrkey_t key; ++ zval *val; + - Z_ADDREF_PP(val); - zvalue = *val; ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(return_value), key.h, key.key, val) ++ { ++ zval zkey, zho; + - MAKE_STD_ZVAL(zkey); - if (key.type == HASH_KEY_IS_LONG) { - ZVAL_LONG(zkey, key.num); ++ if (key.key) { ++ ZVAL_STR_COPY(&zkey, key.key); + } else { - ZVAL_STRINGL(zkey, key.str, key.len - 1, 1); ++ ZVAL_LONG(&zkey, key.h); + } + - MAKE_STD_ZVAL(zho); - object_init_ex(zho, ce); - zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, zkey, zvalue); ++ object_init_ex(&zho, ce); ++ Z_TRY_ADDREF_P(val); ++ zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, &zkey, val); ++ zval_ptr_dtor(val); ++ zval_ptr_dtor(&zkey); + - if (key.type == HASH_KEY_IS_LONG) { - zend_hash_index_update(Z_ARRVAL_P(return_value), key.num, (void *) &zho, sizeof(zval *), NULL); ++ if (key.key) { ++ add_assoc_zval_ex(return_value, key.key->val, key.key->len, &zho); + } else { - zend_hash_update(Z_ARRVAL_P(return_value), key.str, key.len, (void *) &zho, sizeof(zval *), NULL); ++ add_index_zval(return_value, key.h, &zho); + } - - zval_ptr_dtor(&zvalue); - zval_ptr_dtor(&zkey); + } ++ ZEND_HASH_FOREACH_END(); + } + } + } + } + + static zend_function_entry php_http_header_methods[] = { + PHP_ME(HttpHeader, __construct, ai_HttpHeader___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpHeader, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpHeader, __toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpHeader, toString, serialize, ai_HttpHeader_serialize, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeader, unserialize, ai_HttpHeader_unserialize, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeader, match, ai_HttpHeader_match, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeader, negotiate, ai_HttpHeader_negotiate, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeader, getParams, ai_HttpHeader_getParams, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeader, parse, ai_HttpHeader_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_header_class_entry; + + PHP_MINIT_FUNCTION(http_header) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Header", php_http_header_methods); - php_http_header_class_entry = zend_register_internal_class(&ce TSRMLS_CC); - zend_class_implements(php_http_header_class_entry TSRMLS_CC, 1, zend_ce_serializable); - zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_LOOSE"), PHP_HTTP_MATCH_LOOSE TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_CASE"), PHP_HTTP_MATCH_CASE TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_WORD"), PHP_HTTP_MATCH_WORD TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_FULL"), PHP_HTTP_MATCH_FULL TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_STRICT"), PHP_HTTP_MATCH_STRICT TSRMLS_CC); - zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("value"), ZEND_ACC_PUBLIC TSRMLS_CC); ++ php_http_header_class_entry = zend_register_internal_class(&ce); ++ zend_class_implements(php_http_header_class_entry, 1, zend_ce_serializable); ++ zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_LOOSE"), PHP_HTTP_MATCH_LOOSE); ++ zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_CASE"), PHP_HTTP_MATCH_CASE); ++ zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_WORD"), PHP_HTTP_MATCH_WORD); ++ zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_FULL"), PHP_HTTP_MATCH_FULL); ++ zend_declare_class_constant_long(php_http_header_class_entry, ZEND_STRL("MATCH_STRICT"), PHP_HTTP_MATCH_STRICT); ++ zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("name"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_header_class_entry, ZEND_STRL("value"), ZEND_ACC_PUBLIC); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_header.h index 0000000,a2baecb..88ebd10 mode 000000,100644..100644 --- a/src/php_http_header.h +++ b/src/php_http_header.h @@@ -1,0 -1,38 +1,39 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_HEADERS_H + #define PHP_HTTP_HEADERS_H + + #include "php_http_info.h" + -PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data TSRMLS_DC); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_header_parse(const char *header, size_t length, HashTable *headers, php_http_info_callback_t callback_func, void **callback_data); + -PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg TSRMLS_DC); -PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC); ++PHP_HTTP_API void php_http_header_to_callback(HashTable *headers, zend_bool crlf, php_http_pass_format_callback_t cb, void *cb_arg); ++PHP_HTTP_API void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers); + -PHP_HTTP_API zval *php_http_header_value_to_string(zval *header TSRMLS_DC); ++PHP_HTTP_API zend_string *php_http_header_value_to_string(zval *header); ++PHP_HTTP_API zend_string *php_http_header_value_array_to_string(zval *header); + + PHP_HTTP_API zend_class_entry *php_http_header_class_entry; + PHP_MINIT_FUNCTION(http_header); + + #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 --cc src/php_http_header_parser.c index 0000000,46551e2..5dfaedd mode 000000,100644..100644 --- a/src/php_http_header_parser.c +++ b/src/php_http_header_parser.c @@@ -1,0 -1,489 +1,481 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #ifndef DBG_PARSER + # define DBG_PARSER 0 + #endif + + typedef struct php_http_header_parser_state_spec { + php_http_header_parser_state_t state; + unsigned need_data:1; + } php_http_header_parser_state_spec_t; + + static const php_http_header_parser_state_spec_t php_http_header_parser_states[] = { + {PHP_HTTP_HEADER_PARSER_STATE_START, 1}, + {PHP_HTTP_HEADER_PARSER_STATE_KEY, 1}, + {PHP_HTTP_HEADER_PARSER_STATE_VALUE, 1}, + {PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX, 0}, + {PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE, 0}, + {PHP_HTTP_HEADER_PARSER_STATE_DONE, 0} + }; + -php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC) ++php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser) + { + if (!parser) { + parser = emalloc(sizeof(*parser)); + } + memset(parser, 0, sizeof(*parser)); + - TSRMLS_SET_CTX(parser->ts); - + return parser; + } + + php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...) + { + va_list va_args; + unsigned i; + php_http_header_parser_state_t state = 0; + + /* short circuit */ + ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc); + + va_start(va_args, argc); + for (i = 0; i < argc; ++i) { + state = va_arg(va_args, php_http_header_parser_state_t); + zend_ptr_stack_push(&parser->stack, (void *) state); + } + va_end(va_args); + + return state; + } + + php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser) + { + if (parser->stack.top) { + return (php_http_header_parser_state_t) parser->stack.elements[parser->stack.top - 1]; + } + + return PHP_HTTP_HEADER_PARSER_STATE_START; + } + + php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser) + { + if (parser->stack.top) { + return (php_http_header_parser_state_t) zend_ptr_stack_pop(&parser->stack); + } + + return PHP_HTTP_HEADER_PARSER_STATE_START; + } + + void php_http_header_parser_dtor(php_http_header_parser_t *parser) + { + zend_ptr_stack_destroy(&parser->stack); + php_http_info_dtor(&parser->info); + PTR_FREE(parser->_key.str); + PTR_FREE(parser->_val.str); + } + + void php_http_header_parser_free(php_http_header_parser_t **parser) + { + if (*parser) { + php_http_header_parser_dtor(*parser); + efree(*parser); + *parser = NULL; + } + } + + /* NOTE: 'str' has to be null terminated */ -static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str TSRMLS_DC) ++static void php_http_header_parser_error(size_t valid_len, char *str, size_t len, const char *eol_str ) + { - int escaped_len; - char *escaped_str; ++ zend_string *escaped_str = zend_string_init(str, len, 0); + - escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC); ++ escaped_str = php_addcslashes(escaped_str, 1, ZEND_STRL("\x0..\x1F\x7F..\xFF")); + + if (valid_len != len && (!eol_str || (str+valid_len) != eol_str)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%.*s'", str[valid_len], valid_len, escaped_len, escaped_str); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected character '\\%03o' at pos %zu of '%s'", str[valid_len], valid_len, escaped_str->val); + } else if (eol_str) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%.*s'", eol_str - str, escaped_len, escaped_str); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected end of line at pos %zu of '%s'", eol_str - str, escaped_str->val); + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%.*s'", len, escaped_len, escaped_str); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers: unexpected end of input at pos %zu of '%s'", len, escaped_str->val); + } + + efree(escaped_str); + } + + php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg) + { - TSRMLS_FETCH_FROM_CTX(parser->ts); - + while (buffer->used || !php_http_header_parser_states[php_http_header_parser_state_is(parser)].need_data) { + #if DBG_PARSER + const char *state[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"}; + fprintf(stderr, "#HP: %s (avail:%zu, num:%d cleanup:%u)\n", php_http_header_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_header_parser_state_is(parser)], buffer->used, headers?zend_hash_num_elements(headers):0, flags); + _dpf(0, buffer->data, buffer->used); + #endif + switch (php_http_header_parser_state_pop(parser)) { + case PHP_HTTP_HEADER_PARSER_STATE_FAILURE: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse headers"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse headers"); + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE); + + case PHP_HTTP_HEADER_PARSER_STATE_START: { + char *ptr = buffer->data; + + while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { + ++ptr; + } + + php_http_buffer_cut(buffer, 0, ptr - buffer->data); + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY); + break; + } + + case PHP_HTTP_HEADER_PARSER_STATE_KEY: { + const char *colon, *eol_str = NULL; + int eol_len = 0; + + /* fix buffer here, so eol_str pointer doesn't become obsolete afterwards */ + php_http_buffer_fix(buffer); + + if (buffer->data == (eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) { + /* end of headers */ + php_http_buffer_cut(buffer, 0, eol_len); + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_DONE); - } else if (php_http_info_parse(&parser->info, buffer->data TSRMLS_CC)) { ++ } else if (php_http_info_parse(&parser->info, buffer->data)) { + /* new message starting with request/response line */ + if (callback_func) { - callback_func(callback_arg, &headers, &parser->info TSRMLS_CC); ++ callback_func(callback_arg, &headers, &parser->info); + } + php_http_info_dtor(&parser->info); + php_http_buffer_cut(buffer, 0, eol_str + eol_len - buffer->data); + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE); + } else if ((colon = memchr(buffer->data, ':', buffer->used)) && (!eol_str || eol_str > colon)) { + /* header: string */ + size_t valid_len; + + parser->_key.len = colon - buffer->data; + parser->_key.str = estrndup(buffer->data, parser->_key.len); + + valid_len = strspn(parser->_key.str, PHP_HTTP_HEADER_NAME_CHARS); + if (valid_len != parser->_key.len) { - php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str TSRMLS_CC); ++ php_http_header_parser_error(valid_len, parser->_key.str, parser->_key.len, eol_str); + PTR_SET(parser->_key.str, NULL); + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE); + } + while (PHP_HTTP_IS_CTYPE(space, *++colon) && *colon != '\n' && *colon != '\r'); + php_http_buffer_cut(buffer, 0, colon - buffer->data); + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE); + } else if (eol_str || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) { + /* neither reqeust/response line nor 'header:' string, or injected new line or NUL etc. */ - php_http_header_parser_error(strspn(buffer->data, PHP_HTTP_HEADER_NAME_CHARS), buffer->data, buffer->used, eol_str TSRMLS_CC); ++ php_http_header_parser_error(strspn(buffer->data, PHP_HTTP_HEADER_NAME_CHARS), buffer->data, buffer->used, eol_str); + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE); + } else { + /* keep feeding */ + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY); + } + break; + } + + case PHP_HTTP_HEADER_PARSER_STATE_VALUE: { + const char *eol_str; + int eol_len; + + #define SET_ADD_VAL(slen, eol_len) \ + do { \ + const char *ptr = buffer->data; \ + size_t len = slen; \ + \ + while (len > 0 && PHP_HTTP_IS_CTYPE(space, *ptr)) { \ + ++ptr; \ + --len; \ + } \ + while (len > 0 && PHP_HTTP_IS_CTYPE(space, ptr[len - 1])) { \ + --len; \ + } \ + \ + if (len > 0) { \ + if (parser->_val.str) { \ + parser->_val.str = erealloc(parser->_val.str, parser->_val.len + len + 2); \ + parser->_val.str[parser->_val.len++] = ' '; \ + memcpy(&parser->_val.str[parser->_val.len], ptr, len); \ + parser->_val.len += len; \ + parser->_val.str[parser->_val.len] = '\0'; \ + } else { \ + parser->_val.len = len; \ + parser->_val.str = estrndup(ptr, len); \ + } \ + } \ + php_http_buffer_cut(buffer, 0, slen + eol_len); \ + } while (0) + + if ((eol_str = php_http_locate_bin_eol(buffer->data, buffer->used, &eol_len))) { + SET_ADD_VAL(eol_str - buffer->data, eol_len); + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX); + } else if (flags & PHP_HTTP_HEADER_PARSER_CLEANUP) { + if (buffer->used) { + SET_ADD_VAL(buffer->used, 0); + } + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE); + } else { + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE); + } + break; + } + + case PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX: + if (buffer->used && (*buffer->data == ' ' || *buffer->data == '\t')) { + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE); + } else if (buffer->used || (flags & PHP_HTTP_HEADER_PARSER_CLEANUP)) { + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE); + } else { + /* keep feeding */ + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX); + } + break; + + case PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE: + if (parser->_key.str && parser->_val.str) { - zval array, **exist; ++ zval tmp, *exist; + size_t valid_len = strlen(parser->_val.str); + + /* check for truncation */ + if (valid_len != parser->_val.len) { - php_http_header_parser_error(valid_len, parser->_val.str, parser->_val.len, NULL TSRMLS_CC); ++ php_http_header_parser_error(valid_len, parser->_val.str, parser->_val.len, NULL); + + PTR_SET(parser->_key.str, NULL); + PTR_SET(parser->_val.str, NULL); + + return php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_FAILURE); + } + + if (!headers && callback_func) { - callback_func(callback_arg, &headers, NULL TSRMLS_CC); ++ callback_func(callback_arg, &headers, NULL); + } + - INIT_PZVAL_ARRAY(&array, headers); + php_http_pretty_key(parser->_key.str, parser->_key.len, 1, 1); - if (SUCCESS == zend_symtable_find(headers, parser->_key.str, parser->_key.len + 1, (void *) &exist)) { - convert_to_array(*exist); - add_next_index_stringl(*exist, parser->_val.str, parser->_val.len, 0); ++ if ((exist = zend_symtable_str_find(headers, parser->_key.str, parser->_key.len))) { ++ convert_to_array(exist); ++ add_next_index_str(exist, php_http_cs2zs(parser->_val.str, parser->_val.len)); + } else { - add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0); ++ ZVAL_STR(&tmp, php_http_cs2zs(parser->_val.str, parser->_val.len)); ++ zend_symtable_str_update(headers, parser->_key.str, parser->_key.len, &tmp); + } + parser->_val.str = NULL; + } + + PTR_SET(parser->_key.str, NULL); + PTR_SET(parser->_val.str, NULL); + + php_http_header_parser_state_push(parser, 1, PHP_HTTP_HEADER_PARSER_STATE_KEY); + break; + + case PHP_HTTP_HEADER_PARSER_STATE_DONE: + return PHP_HTTP_HEADER_PARSER_STATE_DONE; + } + } + + return php_http_header_parser_state_is(parser); + } + + php_http_header_parser_state_t php_http_header_parser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg) + { + php_http_header_parser_state_t state = PHP_HTTP_HEADER_PARSER_STATE_START; - TSRMLS_FETCH_FROM_CTX(parser->ts); + + if (!buf->data) { + php_http_buffer_resize_ex(buf, 0x1000, 1, 0); + } + while (1) { + size_t justread = 0; + #if DBG_PARSER + const char *states[] = {"START", "KEY", "VALUE", "VALUE_EX", "HEADER_DONE", "DONE"}; + fprintf(stderr, "#SHP: %s (f:%u)\n", states[state], flags); + #endif + /* resize if needed */ + if (buf->free < 0x1000) { + php_http_buffer_resize_ex(buf, 0x1000, 1, 0); + } + switch (state) { + case PHP_HTTP_HEADER_PARSER_STATE_FAILURE: + case PHP_HTTP_HEADER_PARSER_STATE_DONE: + return state; + + default: + /* read line */ + php_stream_get_line(s, buf->data + buf->used, buf->free, &justread); + /* if we fail reading a whole line, try a single char */ + if (!justread) { + int c = php_stream_getc(s); + + if (c != EOF) { + char s[1] = {c}; + justread = php_http_buffer_append(buf, s, 1); + } + } + php_http_buffer_account(buf, justread); + } + + if (justread) { + state = php_http_header_parser_parse(parser, buf, flags, headers, callback_func, callback_arg); + } else if (php_stream_eof(s)) { + return php_http_header_parser_parse(parser, buf, flags | PHP_HTTP_HEADER_PARSER_CLEANUP, headers, callback_func, callback_arg); + } else { + return state; + } + } + + return PHP_HTTP_HEADER_PARSER_STATE_DONE; + } + + zend_class_entry *php_http_header_parser_class_entry; + static zend_object_handlers php_http_header_parser_object_handlers; + -zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_header_parser_object_new(zend_class_entry *ce) + { - return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_header_parser_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC) ++php_http_header_parser_object_t *php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser) + { + php_http_header_parser_object_t *o; + - o = ecalloc(1, sizeof(php_http_header_parser_object_t)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - if (ptr) { - *ptr = o; - } ++ o = ecalloc(1, sizeof(php_http_header_parser_object_t) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + if (parser) { + o->parser = parser; + } else { - o->parser = php_http_header_parser_init(NULL TSRMLS_CC); ++ o->parser = php_http_header_parser_init(NULL); + } + o->buffer = php_http_buffer_new(); + - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_header_parser_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_header_parser_object_handlers; ++ o->zo.handlers = &php_http_header_parser_object_handlers; + - return o->zv; ++ return o; + } + -void php_http_header_parser_object_free(void *object TSRMLS_DC) ++void php_http_header_parser_object_free(zend_object *object) + { - php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object; ++ php_http_header_parser_object_t *o = PHP_HTTP_OBJ(object, NULL); + + if (o->parser) { + php_http_header_parser_free(&o->parser); + } + if (o->buffer) { + php_http_buffer_free(&o->buffer); + } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); ++ zend_object_std_dtor(object); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_getState, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpHeaderParser, getState) + { - php_http_header_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_header_parser_object_t *parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + + zend_parse_parameters_none(); + /* always return the real state */ + RETVAL_LONG(php_http_header_parser_state_is(parser_obj->parser)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_parse, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_ARRAY_INFO(1, headers, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpHeaderParser, parse) + { + php_http_header_parser_object_t *parser_obj; + zval *zmsg; + char *data_str; - int data_len; - long flags; ++ size_t data_len; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); + ++ ZVAL_DEREF(zmsg); + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + php_http_buffer_append(parser_obj->buffer, data_str, data_len); + RETVAL_LONG(php_http_header_parser_parse(parser_obj->parser, parser_obj->buffer, flags, Z_ARRVAL_P(zmsg), NULL, NULL)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpHeaderParser_stream, 0, 0, 3) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_ARRAY_INFO(1, headers, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpHeaderParser, stream) + { + php_http_header_parser_object_t *parser_obj; + zend_error_handling zeh; + zval *zmsg, *zstream; + php_stream *s; - long flags; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &zstream, &flags, &zmsg), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); - php_stream_from_zval(s, &zstream); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh); ++ php_stream_from_zval(s, zstream); ++ zend_restore_error_handling(&zeh); + ++ ZVAL_DEREF(zmsg); + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + RETVAL_LONG(php_http_header_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, Z_ARRVAL_P(zmsg), NULL, NULL)); + } + + static zend_function_entry php_http_header_parser_methods[] = { + PHP_ME(HttpHeaderParser, getState, ai_HttpHeaderParser_getState, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeaderParser, parse, ai_HttpHeaderParser_parse, ZEND_ACC_PUBLIC) + PHP_ME(HttpHeaderParser, stream, ai_HttpHeaderParser_stream, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} + }; + + PHP_MINIT_FUNCTION(http_header_parser) + { + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, "http\\Header", "Parser", php_http_header_parser_methods); - php_http_header_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_header_parser_class_entry = zend_register_internal_class(&ce); + memcpy(&php_http_header_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_http_header_parser_class_entry->create_object = php_http_header_parser_object_new; ++ php_http_header_parser_object_handlers.offset = XtOffsetOf(php_http_header_parser_object_t, zo); + php_http_header_parser_object_handlers.clone_obj = NULL; ++ php_http_header_parser_object_handlers.free_obj = php_http_header_parser_object_free; + - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_HEADER_PARSER_CLEANUP); + - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_HEADER_PARSER_STATE_FAILURE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_HEADER_PARSER_STATE_START); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_KEY"), PHP_HTTP_HEADER_PARSER_STATE_KEY); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE"), PHP_HTTP_HEADER_PARSER_STATE_VALUE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_VALUE_EX"), PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE); ++ zend_declare_class_constant_long(php_http_header_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_HEADER_PARSER_STATE_DONE); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_header_parser.h index 0000000,ed9ecaf..33707bd mode 000000,100644..100644 --- a/src/php_http_header_parser.h +++ b/src/php_http_header_parser.h @@@ -1,0 -1,80 +1,76 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_HEADER_PARSER_H + #define PHP_HTTP_HEADER_PARSER_H + + #include "php_http_info.h" + + typedef enum php_http_header_parser_state { + PHP_HTTP_HEADER_PARSER_STATE_FAILURE = FAILURE, + PHP_HTTP_HEADER_PARSER_STATE_START = 0, + PHP_HTTP_HEADER_PARSER_STATE_KEY, + PHP_HTTP_HEADER_PARSER_STATE_VALUE, + PHP_HTTP_HEADER_PARSER_STATE_VALUE_EX, + PHP_HTTP_HEADER_PARSER_STATE_HEADER_DONE, + PHP_HTTP_HEADER_PARSER_STATE_DONE + } php_http_header_parser_state_t; + + #define PHP_HTTP_HEADER_PARSER_CLEANUP 0x1 + + typedef struct php_http_header_parser { + zend_ptr_stack stack; + php_http_info_t info; + struct { + char *str; + size_t len; + } _key; + struct { + char *str; + size_t len; + } _val; -#ifdef ZTS - void ***ts; -#endif + } php_http_header_parser_t; + -PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser TSRMLS_DC); ++PHP_HTTP_API php_http_header_parser_t *php_http_header_parser_init(php_http_header_parser_t *parser); + PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_push(php_http_header_parser_t *parser, unsigned argc, ...); + PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_is(php_http_header_parser_t *parser); + PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_state_pop(php_http_header_parser_t *parser); + PHP_HTTP_API void php_http_header_parser_dtor(php_http_header_parser_t *parser); + PHP_HTTP_API void php_http_header_parser_free(php_http_header_parser_t **parser); + PHP_HTTP_API php_http_header_parser_state_t php_http_header_parser_parse(php_http_header_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg); + PHP_HTTP_API php_http_header_parser_state_t php_http_headerparser_parse_stream(php_http_header_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, HashTable *headers, php_http_info_callback_t callback_func, void *callback_arg); + + typedef struct php_http_header_parser_object { - zend_object zo; - zend_object_value zv; + php_http_buffer_t *buffer; + php_http_header_parser_t *parser; ++ zend_object zo; + } php_http_header_parser_object_t; + + PHP_HTTP_API zend_class_entry *php_http_header_parser_class_entry; + + PHP_MINIT_FUNCTION(http_header_parser); + -zend_object_value php_http_header_parser_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser, php_http_header_parser_object_t **ptr TSRMLS_DC); -void php_http_header_parser_object_free(void *object TSRMLS_DC); ++zend_object *php_http_header_parser_object_new(zend_class_entry *ce); ++php_http_header_parser_object_t *php_http_header_parser_object_new_ex(zend_class_entry *ce, php_http_header_parser_t *parser); ++void php_http_header_parser_object_free(zend_object *object); + + #endif /* PHP_HTTP_HEADER_PARSER_H */ + + /* + * 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 --cc src/php_http_info.c index 0000000,4fb067f..6eef822 mode 000000,100644..100644 --- a/src/php_http_info.c +++ b/src/php_http_info.c @@@ -1,0 -1,175 +1,175 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -php_http_info_t *php_http_info_init(php_http_info_t *i TSRMLS_DC) ++php_http_info_t *php_http_info_init(php_http_info_t *i) + { + if (!i) { + i = emalloc(sizeof(*i)); + } + + memset(i, 0, sizeof(*i)); + + return i; + } + + void php_http_info_dtor(php_http_info_t *i) + { + switch (i->type) { + case PHP_HTTP_REQUEST: + PTR_SET(PHP_HTTP_INFO(i).request.method, NULL); + PTR_SET(PHP_HTTP_INFO(i).request.url, NULL); + break; + + case PHP_HTTP_RESPONSE: + PTR_SET(PHP_HTTP_INFO(i).response.status, NULL); + break; + + default: + break; + } + } + + void php_http_info_free(php_http_info_t **i) + { + if (*i) { + php_http_info_dtor(*i); + efree(*i); + *i = NULL; + } + } + -php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC) ++php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header) + { + const char *end, *http; + zend_bool free_info = !info; + + /* sane parameter */ + if ((!pre_header) || (!*pre_header)) { + return NULL; + } + + /* where's the end of the line */ + if (!(end = php_http_locate_eol(pre_header, NULL))) { + end = pre_header + strlen(pre_header); + } + + /* there must be HTTP/1.x in the line */ + if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) { + return NULL; + } + - info = php_http_info_init(info TSRMLS_CC); ++ info = php_http_info_init(info); + + /* and nothing than SPACE or NUL after HTTP/X.x */ - if (!php_http_version_parse(&info->http.version, http TSRMLS_CC) ++ if (!php_http_version_parse(&info->http.version, http) + || (http[lenof("HTTP/X.x")] && (!PHP_HTTP_IS_CTYPE(space, http[lenof("HTTP/X.x")])))) { + if (free_info) { + php_http_info_free(&info); + } + return NULL; + } + + #if 0 + { + char *line = estrndup(pre_header, end - pre_header); + fprintf(stderr, "http_parse_info('%s')\n", line); + efree(line); + } + #endif + + /* is response */ + if (pre_header == http) { + const char *status = NULL, *code = http + sizeof("HTTP/X.x"); + + info->type = PHP_HTTP_RESPONSE; - while (' ' == *code) ++code; ++ while (code < end && ' ' == *code) ++code; + if (code && end > code) { + /* rfc7230#3.1.2 The status-code element is a 3-digit integer code */ + PHP_HTTP_INFO(info).response.code = 100*(*code++ - '0'); + PHP_HTTP_INFO(info).response.code += 10*(*code++ - '0'); + PHP_HTTP_INFO(info).response.code += *code++ - '0'; + if (PHP_HTTP_INFO(info).response.code < 100 || PHP_HTTP_INFO(info).response.code > 599) { + if (free_info) { + php_http_info_free(&info); + } + return NULL; + } + status = code; + } else { + PHP_HTTP_INFO(info).response.code = 0; + } + if (status && end > status) { + while (' ' == *status) ++status; + PHP_HTTP_INFO(info).response.status = estrndup(status, end - status); + } else { + PHP_HTTP_INFO(info).response.status = NULL; + } + + return info; + } + + /* is request */ + else if (*(http - 1) == ' ' && (!http[lenof("HTTP/X.x")] || http[lenof("HTTP/X.x")] == '\r' || http[lenof("HTTP/X.x")] == '\n')) { + const char *url = strchr(pre_header, ' '); + + info->type = PHP_HTTP_REQUEST; + if (url && http > url) { + size_t url_len = url - pre_header; + + PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url_len); + + while (' ' == *url) ++url; + while (' ' == *(http-1)) --http; + + if (http > url) { + /* CONNECT presents an authority only */ + if (strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT")) { - PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0 TSRMLS_CC); ++ PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, ~0); + } else { - PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC); ++ PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0); + } + if (!PHP_HTTP_INFO(info).request.url) { + PTR_SET(PHP_HTTP_INFO(info).request.method, NULL); + return NULL; + } + } else { + PTR_SET(PHP_HTTP_INFO(info).request.method, NULL); + return NULL; + } + } else { + PHP_HTTP_INFO(info).request.method = NULL; + PHP_HTTP_INFO(info).request.url = NULL; + } + + return info; + } + + /* some darn header containing HTTP/X.x */ + else { + if (free_info) { + php_http_info_free(&info); + } + 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 --cc src/php_http_info.h index 0000000,4f02908..8a52ee2 mode 000000,100644..100644 --- a/src/php_http_info.h +++ b/src/php_http_info.h @@@ -1,0 -1,76 +1,78 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_INFO_H + #define PHP_HTTP_INFO_H + + #include "php_http_version.h" + #include "php_http_url.h" + + #define PHP_HTTP_INFO_REQUEST_FMT_ARGS(_http_ptr, tmp, eol) "%s %s HTTP/%u.%u" eol, \ + (_http_ptr)->info.request.method?(_http_ptr)->info.request.method:"UNKNOWN", \ + (_http_ptr)->info.request.method&&!strcasecmp((_http_ptr)->info.request.method,"CONNECT")?( \ + (_http_ptr)->info.request.url?php_http_url_authority_to_string((_http_ptr)->info.request.url, &(tmp), NULL):"0"):( \ + (_http_ptr)->info.request.url?php_http_url_to_string((_http_ptr)->info.request.url, &(tmp), NULL, 0):"/"), \ + (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \ + (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1 + + #define PHP_HTTP_INFO_RESPONSE_FMT_ARGS(_http_ptr, tmp, eol) "HTTP/%u.%u %d%s%s" eol, \ + (_http_ptr)->version.major||(_http_ptr)->version.major?(_http_ptr)->version.major:1, \ + (_http_ptr)->version.major||(_http_ptr)->version.minor?(_http_ptr)->version.minor:1, \ + (_http_ptr)->info.response.code?(_http_ptr)->info.response.code:200, \ + (_http_ptr)->info.response.status&&*(_http_ptr)->info.response.status ? " ":"", \ + STR_PTR((_http_ptr)->info.response.status) + + typedef struct php_http_info_data { + union { + /* GET /foo/bar */ + struct { char *method; php_http_url_t *url; } request; + /* 200 Ok */ + struct { unsigned code; char *status; } response; + } info; + php_http_version_t version; + } php_http_info_data_t; + ++#undef PHP_HTTP_REQUEST ++#undef PHP_HTTP_RESPONSE + typedef enum php_http_info_type { + PHP_HTTP_NONE = 0, + PHP_HTTP_REQUEST, + PHP_HTTP_RESPONSE + } php_http_info_type_t; + + #define PHP_HTTP_INFO(ptr) (ptr)->http.info + #define PHP_HTTP_INFO_IMPL(_http, _type) \ + php_http_info_data_t _http; \ + php_http_info_type_t _type; + + typedef struct php_http_info { + PHP_HTTP_INFO_IMPL(http, type) + } php_http_info_t; + -typedef zend_bool (*php_http_info_callback_t)(void **callback_data, HashTable **headers, php_http_info_t *info TSRMLS_DC); ++typedef zend_bool (*php_http_info_callback_t)(void **callback_data, HashTable **headers, php_http_info_t *info); + -PHP_HTTP_API php_http_info_t *php_http_info_init(php_http_info_t *info TSRMLS_DC); -PHP_HTTP_API php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header TSRMLS_DC); ++PHP_HTTP_API php_http_info_t *php_http_info_init(php_http_info_t *info); ++PHP_HTTP_API php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header); + PHP_HTTP_API void php_http_info_dtor(php_http_info_t *info); + PHP_HTTP_API void php_http_info_free(php_http_info_t **info); + + #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 --cc src/php_http_message.c index 0000000,c6b03ff..e6cdbc8 mode 000000,100644..100644 --- a/src/php_http_message.c +++ b/src/php_http_message.c @@@ -1,0 -1,2085 +1,2035 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + static void message_headers(php_http_message_t *msg, php_http_buffer_t *str); + -zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC) ++zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info) + { + php_http_message_t *old = *message; + + /* advance message */ + if (!old || old->type || zend_hash_num_elements(&old->hdrs)) { - (*message) = php_http_message_init(NULL, 0, NULL TSRMLS_CC); ++ (*message) = php_http_message_init(NULL, 0, NULL); + (*message)->parent = old; + if (headers) { + (*headers) = &((*message)->hdrs); + } + } + + if (info) { + php_http_message_set_info(*message, info); + } + + return old != *message; + } + -php_http_message_t *php_http_message_init(php_http_message_t *message, php_http_message_type_t type, php_http_message_body_t *body TSRMLS_DC) ++php_http_message_t *php_http_message_init(php_http_message_t *message, php_http_message_type_t type, php_http_message_body_t *body) + { + if (!message) { + message = emalloc(sizeof(*message)); + } + memset(message, 0, sizeof(*message)); - TSRMLS_SET_CTX(message->ts); + + php_http_message_set_type(message, type); + message->http.version.major = 1; + message->http.version.minor = 1; + zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0); - message->body = body ? body : php_http_message_body_init(NULL, NULL TSRMLS_CC); ++ message->body = body ? body : php_http_message_body_init(NULL, NULL); + + return message; + } + -php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type TSRMLS_DC) ++php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type) + { + int free_msg = !message; + zval *sval, tval; + php_http_message_body_t *mbody; + + switch (type) { + case PHP_HTTP_REQUEST: - mbody = php_http_env_get_request_body(TSRMLS_C); ++ mbody = php_http_env_get_request_body(); + php_http_message_body_addref(mbody); - message = php_http_message_init(message, type, mbody TSRMLS_CC); - if ((sval = php_http_env_get_server_var(ZEND_STRL("SERVER_PROTOCOL"), 1 TSRMLS_CC)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) { - php_http_version_parse(&message->http.version, Z_STRVAL_P(sval) TSRMLS_CC); ++ message = php_http_message_init(message, type, mbody); ++ if ((sval = php_http_env_get_server_var(ZEND_STRL("SERVER_PROTOCOL"), 1)) && !strncmp(Z_STRVAL_P(sval), "HTTP/", lenof("HTTP/"))) { ++ php_http_version_parse(&message->http.version, Z_STRVAL_P(sval)); + } - if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1 TSRMLS_CC))) { ++ if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1))) { + message->http.info.request.method = estrdup(Z_STRVAL_P(sval)); + } - if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1 TSRMLS_CC))) { - message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0 TSRMLS_CC); ++ if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_URI"), 1))) { ++ message->http.info.request.url = php_http_url_parse(Z_STRVAL_P(sval), Z_STRLEN_P(sval), ~0); + } + - php_http_env_get_request_headers(&message->hdrs TSRMLS_CC); ++ php_http_env_get_request_headers(&message->hdrs); + break; + + case PHP_HTTP_RESPONSE: - message = php_http_message_init(NULL, type, NULL TSRMLS_CC); - if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line TSRMLS_CC)) { ++ message = php_http_message_init(NULL, type, NULL); ++ if (!SG(sapi_headers).http_status_line || !php_http_info_parse((php_http_info_t *) &message->http, SG(sapi_headers).http_status_line)) { + if (!(message->http.info.response.code = SG(sapi_headers).http_response_code)) { + message->http.info.response.code = 200; + } + message->http.info.response.status = estrdup(php_http_env_get_response_status_for_code(message->http.info.response.code)); + } + - php_http_env_get_response_headers(&message->hdrs TSRMLS_CC); -#if PHP_VERSION_ID >= 50400 - if (php_output_get_level(TSRMLS_C)) { - if (php_output_get_status(TSRMLS_C) & PHP_OUTPUT_SENT) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_output_get_start_filename(TSRMLS_C), php_output_get_start_lineno(TSRMLS_C)); ++ php_http_env_get_response_headers(&message->hdrs); ++ if (php_output_get_level()) { ++ if (php_output_get_status() & PHP_OUTPUT_SENT) { ++ php_error_docref(NULL, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_output_get_start_filename(), php_output_get_start_lineno()); + goto error; - } else if (SUCCESS != php_output_get_contents(&tval TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body"); ++ } else if (SUCCESS != php_output_get_contents(&tval)) { ++ php_error_docref(NULL, E_WARNING, "Could not fetch response body"); + goto error; + } else { + php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval)); + zval_dtor(&tval); + } + } -#else - if (OG(ob_nesting_level)) { - if (php_get_output_start_filename(TSRMLS_C)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body, output has already been sent at %s:%d", php_get_output_start_filename(TSRMLS_C), php_get_output_start_lineno(TSRMLS_C)); - goto error; - } else if (SUCCESS != php_ob_get_buffer(&tval TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch response body"); - goto error; - } else { - php_http_message_body_append(message->body, Z_STRVAL(tval), Z_STRLEN(tval)); - zval_dtor(&tval); - } - } -#endif + break; + + default: + error: + if (free_msg) { + if (message) { + php_http_message_free(&message); + } + } else { + message = NULL; + } + break; + } + + return message; + } + -php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC) ++php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy) + { + php_http_message_parser_t p; + php_http_buffer_t buf; + unsigned flags = PHP_HTTP_MESSAGE_PARSER_CLEANUP; + int free_msg; + + php_http_buffer_from_string_ex(&buf, str, len); - php_http_message_parser_init(&p TSRMLS_CC); ++ php_http_message_parser_init(&p); + + if ((free_msg = !msg)) { - msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC); ++ msg = php_http_message_init(NULL, 0, NULL); + } + + if (greedy) { + flags |= PHP_HTTP_MESSAGE_PARSER_GREEDY; + } + if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse(&p, &buf, flags, &msg)) { + if (free_msg) { + php_http_message_free(&msg); + } + msg = NULL; + } + + php_http_message_parser_dtor(&p); + php_http_buffer_dtor(&buf); + + return msg; + } + -zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join) ++zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len) + { - zval *ret = NULL, **header; ++ zval *ret; + char *key; + ALLOCA_FLAG(free_key); + + key = do_alloca(key_len + 1, free_key); ++ + memcpy(key, key_str, key_len); + key[key_len] = '\0'; + php_http_pretty_key(key, key_len, 1, 1); + - if (SUCCESS == zend_symtable_find(&msg->hdrs, key, key_len + 1, (void *) &header)) { - if (join && Z_TYPE_PP(header) == IS_ARRAY) { - TSRMLS_FETCH_FROM_CTX(msg->ts); - - ret = php_http_header_value_to_string(*header TSRMLS_CC); - } else { - Z_ADDREF_PP(header); - ret = *header; - } - } ++ ret = zend_symtable_str_find(&msg->hdrs, key, key_len); + + free_alloca(key, free_key); + + return ret; + } + + zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary) + { - zval *ct = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1); ++ zend_string *ct = php_http_message_header_string(msg, ZEND_STRL("Content-Type")); + zend_bool is_multipart = 0; - TSRMLS_FETCH_FROM_CTX(msg->ts); + + if (ct) { + php_http_params_opts_t popts; + HashTable params; + + ZEND_INIT_SYMTABLE(¶ms); + php_http_params_opts_default_get(&popts); - popts.input.str = Z_STRVAL_P(ct); - popts.input.len = Z_STRLEN_P(ct); ++ popts.input.str = ct->val; ++ popts.input.len = ct->len; + - if (php_http_params_parse(¶ms, &popts TSRMLS_CC)) { - zval **cur, **arg; - char *ct_str; ++ if (php_http_params_parse(¶ms, &popts)) { ++ zval *cur, *arg; ++ zend_string *ct_str; ++ zend_ulong index; + + zend_hash_internal_pointer_reset(¶ms); + - if (SUCCESS == zend_hash_get_current_data(¶ms, (void *) &cur) - && Z_TYPE_PP(cur) == IS_ARRAY - && HASH_KEY_IS_STRING == zend_hash_get_current_key(¶ms, &ct_str, NULL, 0) ++ if ((cur = zend_hash_get_current_data(¶ms)) ++ && (Z_TYPE_P(cur) == IS_ARRAY) ++ && (HASH_KEY_IS_STRING == zend_hash_get_current_key(¶ms, &ct_str, &index)) + ) { - if (php_http_match(ct_str, "multipart", PHP_HTTP_MATCH_WORD)) { ++ if (php_http_match(ct_str->val, "multipart", PHP_HTTP_MATCH_WORD)) { + is_multipart = 1; + + /* get boundary */ + if (boundary - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(cur), ZEND_STRS("arguments"), (void *) &arg) - && Z_TYPE_PP(arg) == IS_ARRAY ++ && (arg = zend_hash_str_find(Z_ARRVAL_P(cur), ZEND_STRL("arguments"))) ++ && Z_TYPE_P(arg) == IS_ARRAY + ) { - zval **val; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); ++ zval *val; ++ php_http_arrkey_t key; + - FOREACH_KEYVAL(pos, *arg, key, val) { - if (key.type == HASH_KEY_IS_STRING && !strcasecmp(key.str, "boundary")) { - zval *bnd = php_http_ztyp(IS_STRING, *val); ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arg), key.h, key.key, val) ++ { ++ if (key.key && key.key->len == lenof("boundary") && !strcasecmp(key.key->val, "boundary")) { ++ zend_string *bnd = zval_get_string(val); + - if (Z_STRLEN_P(bnd)) { - *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd)); ++ if (bnd->len) { ++ *boundary = estrndup(bnd->val, bnd->len); + } - zval_ptr_dtor(&bnd); ++ zend_string_release(bnd); + } + } ++ ZEND_HASH_FOREACH_END(); + } + } + } + } + zend_hash_destroy(¶ms); - zval_ptr_dtor(&ct); ++ zend_string_release(ct); + } + + return is_multipart; + } + + /* */ + void php_http_message_set_type(php_http_message_t *message, php_http_message_type_t type) + { + /* just act if different */ + if (type != message->type) { + + /* free request info */ + switch (message->type) { + case PHP_HTTP_REQUEST: + PTR_FREE(message->http.info.request.method); + PTR_FREE(message->http.info.request.url); + break; + + case PHP_HTTP_RESPONSE: + PTR_FREE(message->http.info.response.status); + break; + + default: + break; + } + + message->type = type; + memset(&message->http, 0, sizeof(message->http)); + } + } + + void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info) + { + php_http_message_set_type(message, info->type); + message->http.version = info->http.version; + switch (message->type) { + case PHP_HTTP_REQUEST: + PTR_SET(PHP_HTTP_INFO(message).request.url, PHP_HTTP_INFO(info).request.url ? php_http_url_copy(PHP_HTTP_INFO(info).request.url, 0) : NULL); + PTR_SET(PHP_HTTP_INFO(message).request.method, PHP_HTTP_INFO(info).request.method ? estrdup(PHP_HTTP_INFO(info).request.method) : NULL); + break; + + case PHP_HTTP_RESPONSE: + PHP_HTTP_INFO(message).response.code = PHP_HTTP_INFO(info).response.code; + PTR_SET(PHP_HTTP_INFO(message).response.status, PHP_HTTP_INFO(info).response.status ? estrdup(PHP_HTTP_INFO(info).response.status) : NULL); + break; + + default: + break; + } + } + + void php_http_message_update_headers(php_http_message_t *msg) + { - zval *h; ++ zval h; + size_t size; ++ zend_string *cl; + + if (php_http_message_body_stream(msg->body)->readfilters.head) { + /* if a read stream filter is attached to the body the caller must also care for the headers */ - } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Range"), 0))) { ++ } else if (php_http_message_header(msg, ZEND_STRL("Content-Range"))) { + /* don't mess around with a Content-Range message */ - zval_ptr_dtor(&h); + } else if ((size = php_http_message_body_size(msg->body))) { - MAKE_STD_ZVAL(h); - ZVAL_LONG(h, size); - zend_hash_update(&msg->hdrs, "Content-Length", sizeof("Content-Length"), &h, sizeof(zval *), NULL); ++ ZVAL_LONG(&h, size); ++ zend_hash_str_update(&msg->hdrs, "Content-Length", lenof("Content-Length"), &h); + + if (msg->body->boundary) { + char *str; + size_t len; ++ zend_string *ct; + - if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) { ++ if (!(ct = php_http_message_header_string(msg, ZEND_STRL("Content-Type")))) { + len = spprintf(&str, 0, "multipart/form-data; boundary=\"%s\"", msg->body->boundary); - MAKE_STD_ZVAL(h); - ZVAL_STRINGL(h, str, len, 0); - zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL); - } else if (!php_http_match(Z_STRVAL_P(h), "boundary=", PHP_HTTP_MATCH_WORD)) { - zval_dtor(h); - Z_STRLEN_P(h) = spprintf(&Z_STRVAL_P(h), 0, "%s; boundary=\"%s\"", Z_STRVAL_P(h), msg->body->boundary); - zend_hash_update(&msg->hdrs, "Content-Type", sizeof("Content-Type"), &h, sizeof(zval *), NULL); ++ ZVAL_STR(&h, php_http_cs2zs(str, len)); ++ zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h); ++ } else if (!php_http_match(ct->val, "boundary=", PHP_HTTP_MATCH_WORD)) { ++ len = spprintf(&str, 0, "%s; boundary=\"%s\"", ct->val, msg->body->boundary); ++ ZVAL_STR(&h, php_http_cs2zs(str, len)); ++ zend_hash_str_update(&msg->hdrs, "Content-Type", lenof("Content-Type"), &h); ++ zend_string_release(ct); + } else { - zval_ptr_dtor(&h); ++ zend_string_release(ct); + } + } - } else if ((h = php_http_message_header(msg, ZEND_STRL("Content-Length"), 1))) { - zval *h_cpy = php_http_ztyp(IS_LONG, h); - - zval_ptr_dtor(&h); - if (Z_LVAL_P(h_cpy)) { ++ } else if ((cl = php_http_message_header_string(msg, ZEND_STRL("Content-Length")))) { ++ if (!zend_string_equals_literal(cl, "0")) { + /* body->size == 0, so get rid of old Content-Length */ - zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length")); ++ zend_hash_str_del(&msg->hdrs, ZEND_STRL("Content-Length")); + } - zval_ptr_dtor(&h_cpy); ++ zend_string_release(cl); + } + } + + static void message_headers(php_http_message_t *msg, php_http_buffer_t *str) + { + char *tmp = NULL; - TSRMLS_FETCH_FROM_CTX(msg->ts); + + switch (msg->type) { + case PHP_HTTP_REQUEST: + php_http_buffer_appendf(str, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF)); + PTR_FREE(tmp); + break; + + case PHP_HTTP_RESPONSE: + php_http_buffer_appendf(str, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&msg->http, tmp, PHP_HTTP_CRLF)); + PTR_FREE(tmp); + break; + + default: + break; + } + + php_http_message_update_headers(msg); - php_http_header_to_string(str, &msg->hdrs TSRMLS_CC); ++ php_http_header_to_string(str, &msg->hdrs); + } + + void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg) + { + php_http_buffer_t str; + + php_http_buffer_init_ex(&str, 0x1000, 0); + message_headers(msg, &str); + cb(cb_arg, str.data, str.used); + php_http_buffer_dtor(&str); + + if (php_http_message_body_size(msg->body)) { + cb(cb_arg, ZEND_STRL(PHP_HTTP_CRLF)); + php_http_message_body_to_callback(msg->body, cb, cb_arg, 0, 0); + } + } + + void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length) + { + php_http_buffer_t str; + char *data; + + php_http_buffer_init_ex(&str, 0x1000, 0); + message_headers(msg, &str); + if (php_http_message_body_size(msg->body)) { + php_http_buffer_appends(&str, PHP_HTTP_CRLF); + php_http_message_body_to_callback(msg->body, (php_http_pass_callback_t) php_http_buffer_append, &str, 0, 0); + } + + data = php_http_buffer_data(&str, string, length); + if (!string) { + efree(data); + } + + php_http_buffer_dtor(&str); + } + + void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length) + { + char *buf; + php_http_buffer_t str; + php_http_message_t *msg; + + php_http_buffer_init(&str); + + msg = message = php_http_message_reverse(message); + do { + php_http_message_to_callback(message, (php_http_pass_callback_t) php_http_buffer_append, &str); + php_http_buffer_appends(&str, PHP_HTTP_CRLF); + } while ((message = message->parent)); + php_http_message_reverse(msg); + + buf = php_http_buffer_data(&str, string, length); + if (!string) { + efree(buf); + } + + php_http_buffer_dtor(&str); + } + + php_http_message_t *php_http_message_reverse(php_http_message_t *msg) + { - int i, c = 0; - - php_http_message_count(c, msg); ++ size_t i, c = php_http_message_count(msg); + + if (c > 1) { + php_http_message_t *tmp = msg, **arr; + + arr = ecalloc(c, sizeof(**arr)); + for (i = 0; i < c; ++i) { + arr[i] = tmp; + tmp = tmp->parent; + } + arr[0]->parent = NULL; + for (i = 0; i < c-1; ++i) { + arr[i+1]->parent = arr[i]; + } + + msg = arr[c-1]; + efree(arr); + } + + return msg; + } + -php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two) ++php_http_message_t *php_http_message_zip(php_http_message_t *dst, php_http_message_t *src) + { - php_http_message_t *dst = php_http_message_copy(one, NULL), *src = php_http_message_copy(two, NULL), *tmp_dst, *tmp_src, *ret = dst; ++ php_http_message_t *tmp_dst, *tmp_src, *ret = dst; + - while(dst && src) { ++ while (dst && src) { + tmp_dst = dst->parent; + tmp_src = src->parent; + dst->parent = src; + if (tmp_dst) { + src->parent = tmp_dst; + } + src = tmp_src; + dst = tmp_dst; + } + + return ret; + } + + php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents) + { + php_http_message_t *temp, *copy = NULL; + php_http_info_t info; - TSRMLS_FETCH_FROM_CTX(from->ts); + + if (from) { + info.type = from->type; + info.http = from->http; + - copy = temp = php_http_message_init(to, 0, php_http_message_body_copy(from->body, NULL) TSRMLS_CC); ++ copy = temp = php_http_message_init(to, 0, php_http_message_body_copy(from->body, NULL)); + php_http_message_set_info(temp, &info); - zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref); + + if (parents) while (from->parent) { + info.type = from->parent->type; + info.http = from->parent->http; + - temp->parent = php_http_message_init(NULL, 0, php_http_message_body_copy(from->parent->body, NULL) TSRMLS_CC); ++ temp->parent = php_http_message_init(NULL, 0, php_http_message_body_copy(from->parent->body, NULL)); + php_http_message_set_info(temp->parent, &info); - zend_hash_copy(&temp->parent->hdrs, &from->parent->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ array_copy(&from->parent->hdrs, &temp->parent->hdrs); + + temp = temp->parent; + from = from->parent; + } + } + + return copy; + } + -php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to) -{ - return php_http_message_copy_ex(from, to, 1); -} - + void php_http_message_dtor(php_http_message_t *message) + { + if (message) { + zend_hash_destroy(&message->hdrs); + php_http_message_body_free(&message->body); + + switch (message->type) { + case PHP_HTTP_REQUEST: + PTR_SET(message->http.info.request.method, NULL); + PTR_SET(message->http.info.request.url, NULL); + break; + + case PHP_HTTP_RESPONSE: + PTR_SET(message->http.info.response.status, NULL); + break; + + default: + break; + } + } + } + + void php_http_message_free(php_http_message_t **message) + { + if (*message) { + if ((*message)->parent) { + php_http_message_free(&(*message)->parent); + } + php_http_message_dtor(*message); + efree(*message); + *message = NULL; + } + } + -static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC); -static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC); -static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC); ++static zval *php_http_message_object_read_prop(zval *object, zval *member, int type, void **cache_slot, zval *rv); ++static void php_http_message_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot); + + static zend_object_handlers php_http_message_object_handlers; + static HashTable php_http_message_object_prophandlers; + -typedef void (*php_http_message_object_prophandler_func_t)(php_http_message_object_t *o, zval *v TSRMLS_DC); ++static void php_http_message_object_prophandler_hash_dtor(zval *pData) ++{ ++ pefree(Z_PTR_P(pData), 1); ++} ++ ++typedef void (*php_http_message_object_prophandler_func_t)(php_http_message_object_t *o, zval *v); + + typedef struct php_http_message_object_prophandler { + php_http_message_object_prophandler_func_t read; + php_http_message_object_prophandler_func_t write; + } php_http_message_object_prophandler_t; + + static ZEND_RESULT_CODE php_http_message_object_add_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_func_t read, php_http_message_object_prophandler_func_t write) { + php_http_message_object_prophandler_t h = { read, write }; - return zend_hash_add(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) &h, sizeof(h), NULL); ++ if (!zend_hash_str_add_mem(&php_http_message_object_prophandlers, prop_str, prop_len, (void *) &h, sizeof(h))) { ++ return FAILURE; ++ } ++ return SUCCESS; + } -static ZEND_RESULT_CODE php_http_message_object_get_prophandler(const char *prop_str, size_t prop_len, php_http_message_object_prophandler_t **handler) { - return zend_hash_find(&php_http_message_object_prophandlers, prop_str, prop_len + 1, (void *) handler); ++static php_http_message_object_prophandler_t *php_http_message_object_get_prophandler(zend_string *name_str) { ++ return zend_hash_str_find_ptr(&php_http_message_object_prophandlers, name_str->val, name_str->len); + } -static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value) { + RETVAL_LONG(obj->message->type); + } -static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - zval *cpy = php_http_ztyp(IS_LONG, value); - php_http_message_set_type(obj->message, Z_LVAL_P(cpy)); - zval_ptr_dtor(&cpy); ++static void php_http_message_object_prophandler_set_type(php_http_message_object_t *obj, zval *value) { ++ php_http_message_set_type(obj->message, zval_get_long(value)); + } -static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_request_method(php_http_message_object_t *obj, zval *return_value) { + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) { - RETVAL_STRING(obj->message->http.info.request.method, 1); ++ RETVAL_STRING(obj->message->http.info.request.method); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) { ++static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value) { + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) { - zval *cpy = php_http_ztyp(IS_STRING, value); - PTR_SET(obj->message->http.info.request.method, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy))); - zval_ptr_dtor(&cpy); ++ zend_string *zs = zval_get_string(value); ++ PTR_SET(obj->message->http.info.request.method, estrndup(zs->val, zs->len)); ++ zend_string_release(zs); + } + } -static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value) { + char *url_str; + size_t url_len; + + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.url && php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0)) { - RETVAL_STRINGL(url_str, url_len, 0); ++ RETVAL_STR(php_http_cs2zs(url_str, url_len)); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) { ++static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value) { + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message)) { - PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0 TSRMLS_CC)); ++ PTR_SET(obj->message->http.info.request.url, php_http_url_from_zval(value, ~0)); + } + } -static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value) { + if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) { - RETVAL_STRING(obj->message->http.info.response.status, 1); ++ RETVAL_STRING(obj->message->http.info.response.status); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) { ++static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value) { + if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) { - zval *cpy = php_http_ztyp(IS_STRING, value); - PTR_SET(obj->message->http.info.response.status, estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy))); - zval_ptr_dtor(&cpy); ++ zend_string *zs = zval_get_string(value); ++ PTR_SET(obj->message->http.info.response.status, estrndup(zs->val, zs->len)); ++ zend_string_release(zs); + } + } -static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value) { + if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) { + RETVAL_LONG(obj->message->http.info.response.code); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_response_code(php_http_message_object_t *obj, zval *value TSRMLS_DC) { ++static void php_http_message_object_prophandler_set_response_code(php_http_message_object_t *obj, zval *value) { + if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message)) { - zval *cpy = php_http_ztyp(IS_LONG, value); - obj->message->http.info.response.code = Z_LVAL_P(cpy); ++ obj->message->http.info.response.code = zval_get_long(value); + PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(obj->message->http.info.response.code))); - zval_ptr_dtor(&cpy); + } + } -static void php_http_message_object_prophandler_get_http_version(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_http_version(php_http_message_object_t *obj, zval *return_value) { + char *version_str; + size_t version_len; + - php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL TSRMLS_CC); - RETVAL_STRINGL(version_str, version_len, 0); ++ php_http_version_to_string(&obj->message->http.version, &version_str, &version_len, NULL, NULL); ++ RETVAL_STR(php_http_cs2zs(version_str, version_len)); + } -static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - zval *cpy = php_http_ztyp(IS_STRING, value); - php_http_version_parse(&obj->message->http.version, Z_STRVAL_P(cpy) TSRMLS_CC); - zval_ptr_dtor(&cpy); ++static void php_http_message_object_prophandler_set_http_version(php_http_message_object_t *obj, zval *value) { ++ zend_string *zs = zval_get_string(value); ++ php_http_version_parse(&obj->message->http.version, zs->val); ++ zend_string_release(zs); + } -static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_headers(php_http_message_object_t *obj, zval *return_value ) { + array_init(return_value); - zend_hash_copy(Z_ARRVAL_P(return_value), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ++ array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value)); + } -static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - zval *cpy = php_http_ztyp(IS_ARRAY, value); ++static void php_http_message_object_prophandler_set_headers(php_http_message_object_t *obj, zval *value) { ++ HashTable *headers; ++ zval *orig_value = value; ++ ++ if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) { ++ convert_to_array_ex(value); ++ } ++ headers = HASH_OF(value); + + zend_hash_clean(&obj->message->hdrs); - zend_hash_copy(&obj->message->hdrs, Z_ARRVAL_P(cpy), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - zval_ptr_dtor(&cpy); ++ array_copy(headers, &obj->message->hdrs); ++ ++ if (orig_value != value) { ++ zval_ptr_dtor(value); ++ } + } -static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value) { + if (obj->body) { - RETVAL_OBJVAL(obj->body->zv, 1); ++ RETVAL_OBJECT(&obj->body->zo, 1); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_body(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - php_http_message_object_set_body(obj, value TSRMLS_CC); ++static void php_http_message_object_prophandler_set_body(php_http_message_object_t *obj, zval *value) { ++ php_http_message_object_set_body(obj, value); + } -static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { ++static void php_http_message_object_prophandler_get_parent_message(php_http_message_object_t *obj, zval *return_value) { + if (obj->message->parent) { - RETVAL_OBJVAL(obj->parent->zv, 1); ++ RETVAL_OBJECT(&obj->parent->zo, 1); + } else { + RETVAL_NULL(); + } + } -static void php_http_message_object_prophandler_set_parent_message(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry TSRMLS_CC)) { - php_http_message_object_t *parent_obj = zend_object_store_get_object(value TSRMLS_CC); ++static void php_http_message_object_prophandler_set_parent_message(php_http_message_object_t *obj, zval *value) { ++ if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), php_http_message_class_entry)) { ++ php_http_message_object_t *parent_obj = PHP_HTTP_OBJ(NULL, value); + + if (obj->message->parent) { - zend_objects_store_del_ref_by_handle(obj->parent->zv.handle TSRMLS_CC); ++ zend_objects_store_del(&obj->parent->zo); + } - Z_OBJ_ADDREF_P(value); ++ Z_ADDREF_P(value); + obj->parent = parent_obj; + obj->message->parent = parent_obj->message; + } + } + + #define PHP_HTTP_MESSAGE_OBJECT_INIT(obj) \ + do { \ + if (!obj->message) { \ - obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC); \ ++ obj->message = php_http_message_init(NULL, 0, NULL); \ + } \ + } while(0) + + -void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC) ++void php_http_message_object_reverse(zval *zmsg, zval *return_value) + { - int i = 0; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ size_t i; ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, zmsg); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + /* count */ - php_http_message_count(i, obj->message); ++ i = php_http_message_count(obj->message); + + if (i > 1) { + php_http_message_object_t **objects; + int last; + + objects = ecalloc(i, sizeof(**objects)); + + /* we are the first message */ + objects[0] = obj; + + /* fetch parents */ + for (i = 1; obj->parent; ++i) { + objects[i] = obj = obj->parent; + } + + /* reorder parents */ + for (last = --i; i; --i) { + objects[i]->message->parent = objects[i-1]->message; + objects[i]->parent = objects[i-1]; + } + + objects[0]->message->parent = NULL; + objects[0]->parent = NULL; + + /* add ref, because we previously have not been a parent message */ - Z_OBJ_ADDREF_P(getThis()); - RETVAL_OBJVAL(objects[last]->zv, 0); ++ Z_ADDREF_P(zmsg); ++ /* no addref, because we've been a parent message previously */ ++ RETVAL_OBJECT(&objects[last]->zo, 0); + + efree(objects); + } else { - RETURN_ZVAL(getThis(), 1, 0); ++ RETURN_ZVAL(zmsg, 1, 0); + } + } + -void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC) ++void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top) + { - zval m; + php_http_message_t *save_parent_msg = NULL; - php_http_message_object_t *save_parent_obj = NULL, *obj = zend_object_store_get_object(this_ptr TSRMLS_CC); - php_http_message_object_t *prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC); - - INIT_PZVAL(&m); - m.type = IS_OBJECT; ++ php_http_message_object_t *save_parent_obj = NULL, *obj = PHP_HTTP_OBJ(NULL, this_ptr); ++ php_http_message_object_t *prepend_obj = PHP_HTTP_OBJ(NULL, prepend); + + if (!top) { + save_parent_obj = obj->parent; + save_parent_msg = obj->message->parent; + } else { + /* iterate to the most parent object */ + while (obj->parent) { + obj = obj->parent; + } + } + + /* prepend */ + obj->parent = prepend_obj; + obj->message->parent = prepend_obj->message; + + /* add ref */ - zend_objects_store_add_ref(prepend TSRMLS_CC); ++ Z_ADDREF_P(prepend); + + if (!top) { + prepend_obj->parent = save_parent_obj; + prepend_obj->message->parent = save_parent_msg; + } + } + -ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *msg_obj, zval *zbody TSRMLS_DC) ++ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *msg_obj, zval *zbody) + { - zval *tmp = NULL; + php_stream *s; - zend_object_value ov; ++ zend_string *body_str; + php_http_message_body_t *body; + php_http_message_body_object_t *body_obj; + + switch (Z_TYPE_P(zbody)) { + case IS_RESOURCE: - php_stream_from_zval_no_verify(s, &zbody); ++ php_stream_from_zval_no_verify(s, zbody); + if (!s) { + php_http_throw(unexpected_val, "The stream is not a valid resource", NULL); + return FAILURE; + } + + is_resource: + - body = php_http_message_body_init(NULL, s TSRMLS_CC); - if (SUCCESS != php_http_new(&ov, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, body, NULL TSRMLS_CC)) { ++ body = php_http_message_body_init(NULL, s); ++ if (!(body_obj = php_http_message_body_object_new_ex(php_http_message_body_class_entry, body))) { + php_http_message_body_free(&body); + return FAILURE; + } - MAKE_STD_ZVAL(tmp); - ZVAL_OBJVAL(tmp, ov, 0); - zbody = tmp; + break; + + case IS_OBJECT: - if (instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) { - Z_OBJ_ADDREF_P(zbody); ++ if (instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry)) { ++ Z_ADDREF_P(zbody); ++ body_obj = PHP_HTTP_OBJ(NULL, zbody); + break; + } + /* no break */ + + default: - tmp = php_http_ztyp(IS_STRING, zbody); ++ body_str = zval_get_string(zbody); + s = php_stream_temp_new(); - php_stream_write(s, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - zval_ptr_dtor(&tmp); - tmp = NULL; ++ php_stream_write(s, body_str->val, body_str->len); ++ zend_string_release(body_str); + goto is_resource; + + } + - body_obj = zend_object_store_get_object(zbody TSRMLS_CC); + if (!body_obj->body) { - body_obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); ++ body_obj->body = php_http_message_body_init(NULL, NULL); + } + if (msg_obj->body) { - zend_objects_store_del_ref_by_handle(msg_obj->body->zv.handle TSRMLS_CC); ++ zend_objects_store_del(&msg_obj->body->zo); + } + if (msg_obj->message) { + php_http_message_body_free(&msg_obj->message->body); - msg_obj->message->body = php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC); ++ msg_obj->message->body = body_obj->body; + } else { - msg_obj->message = php_http_message_init(NULL, 0, php_http_message_body_init(&body_obj->body, NULL TSRMLS_CC) TSRMLS_CC); ++ msg_obj->message = php_http_message_init(NULL, 0, body_obj->body); + } ++ php_http_message_body_addref(body_obj->body); + msg_obj->body = body_obj; + - if (tmp) { - FREE_ZVAL(tmp); - } + return SUCCESS; + } + + ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj) + { - TSRMLS_FETCH_FROM_CTX(obj->message->ts); - + php_http_message_body_addref(obj->message->body); - return php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, obj->message->body, (void *) &obj->body TSRMLS_CC); ++ return php_http_new((void *) &obj->body, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, obj->message->body); + } + -zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_message_object_new(zend_class_entry *ce) + { - return php_http_message_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_message_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC) ++php_http_message_object_t *php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg) + { + php_http_message_object_t *o; + - o = ecalloc(1, sizeof(php_http_message_object_t)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - if (ptr) { - *ptr = o; - } ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + if (msg) { + o->message = msg; + if (msg->parent) { - php_http_message_object_new_ex(ce, msg->parent, &o->parent TSRMLS_CC); ++ o->parent = php_http_message_object_new_ex(ce, msg->parent); + } - php_http_message_body_object_new_ex(php_http_message_body_class_entry, php_http_message_body_init(&msg->body, NULL TSRMLS_CC), &o->body TSRMLS_CC); ++ o->body = php_http_message_body_object_new_ex(php_http_message_body_class_entry, php_http_message_body_init(&msg->body, NULL)); + } + - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_message_object_handlers; ++ o->zo.handlers = &php_http_message_object_handlers; + - return o->zv; ++ return o; + } + -zend_object_value php_http_message_object_clone(zval *this_ptr TSRMLS_DC) ++zend_object *php_http_message_object_clone(zval *this_ptr) + { - zend_object_value new_ov; + php_http_message_object_t *new_obj = NULL; - php_http_message_object_t *old_obj = zend_object_store_get_object(this_ptr TSRMLS_CC); ++ php_http_message_object_t *old_obj = PHP_HTTP_OBJ(NULL, this_ptr); + - new_ov = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL), &new_obj TSRMLS_CC); - zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(this_ptr) TSRMLS_CC); ++ new_obj = php_http_message_object_new_ex(old_obj->zo.ce, php_http_message_copy(old_obj->message, NULL)); ++ zend_objects_clone_members(&new_obj->zo, &old_obj->zo); + - return new_ov; ++ return &new_obj->zo; + } + -void php_http_message_object_free(void *object TSRMLS_DC) ++void php_http_message_object_free(zend_object *object) + { - php_http_message_object_t *o = (php_http_message_object_t *) object; ++ php_http_message_object_t *o = PHP_HTTP_OBJ(object, NULL); + - if (o->iterator) { ++ if (!Z_ISUNDEF(o->iterator)) { + zval_ptr_dtor(&o->iterator); - o->iterator = NULL; ++ ZVAL_UNDEF(&o->iterator); + } + if (o->message) { + /* do NOT free recursivly */ + php_http_message_dtor(o->message); + efree(o->message); + o->message = NULL; + } + if (o->parent) { - zend_objects_store_del_ref_by_handle(o->parent->zv.handle TSRMLS_CC); ++ if (GC_REFCOUNT(&o->parent->zo) == 1) { ++ zend_objects_store_del(&o->parent->zo); ++ } ++ zend_objects_store_del(&o->parent->zo); + o->parent = NULL; + } + if (o->body) { - zend_objects_store_del_ref_by_handle(o->body->zv.handle TSRMLS_CC); ++ if (GC_REFCOUNT(&o->body->zo) == 1) { ++ zend_objects_store_del(&o->body->zo); ++ } ++ zend_objects_store_del(&o->body->zo); + o->body = NULL; + } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); ++ zend_object_std_dtor(object); + } + -static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) ++static zval *php_http_message_object_read_prop(zval *object, zval *member, int type, void **cache_slot, zval *tmp) + { - php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - php_http_message_object_prophandler_t *handler; - zval *return_value, *copy = php_http_ztyp(IS_STRING, member); ++ zval *return_value; ++ zend_string *member_name = zval_get_string(member); ++ php_http_message_object_prophandler_t *handler = php_http_message_object_get_prophandler(member_name); + - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); ++ if (!handler || type == BP_VAR_R || type == BP_VAR_IS) { ++ return_value = zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp); + - if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) { - ALLOC_ZVAL(return_value); - Z_SET_REFCOUNT_P(return_value, 0); - Z_UNSET_ISREF_P(return_value); ++ if (handler) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, object); + - if (type == BP_VAR_R) { - handler->read(obj, return_value TSRMLS_CC); - } else { - php_property_proxy_t *proxy = php_property_proxy_init(object, Z_STRVAL_P(copy), Z_STRLEN_P(copy) TSRMLS_CC); - RETVAL_OBJVAL(php_property_proxy_object_new_ex(php_property_proxy_get_class_entry(), proxy, NULL TSRMLS_CC), 0); ++ PHP_HTTP_MESSAGE_OBJECT_INIT(obj); ++ handler->read(obj, tmp); ++ ++ zval_ptr_dtor(return_value); ++ ZVAL_COPY_VALUE(return_value, tmp); + } ++ zend_string_release(member_name); ++ return return_value; + } else { - return_value = zend_get_std_object_handlers()->read_property(object, member, type PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); - } ++ php_property_proxy_t *proxy; ++ php_property_proxy_object_t *proxy_obj; + - zval_ptr_dtor(©); ++ proxy = php_property_proxy_init(object, member_name); ++ proxy_obj = php_property_proxy_object_new_ex(NULL, proxy); + - return return_value; ++ ZVAL_OBJ(tmp, &proxy_obj->zo); ++ zend_string_release(member_name); ++ return tmp; ++ } + } + -static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) ++static void php_http_message_object_write_prop(zval *object, zval *member, zval *value, void **cache_slot) + { - php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, object); + php_http_message_object_prophandler_t *handler; - zval *copy = php_http_ztyp(IS_STRING, member); ++ zend_string *member_name = zval_get_string(member); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - if (SUCCESS == php_http_message_object_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) { - handler->write(obj, value TSRMLS_CC); ++ if ((handler = php_http_message_object_get_prophandler(member_name))) { ++ handler->write(obj, value); + } else { - zend_get_std_object_handlers()->write_property(object, member, value PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); ++ zend_get_std_object_handlers()->write_property(object, member, value, cache_slot); + } + - zval_ptr_dtor(©); ++ zend_string_release(member_name); + } + -static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) ++static HashTable *php_http_message_object_get_debug_info(zval *object, int *is_temp) + { - zval *headers; - php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC); - zval array, *parent, *body; ++ zval tmp; ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, object); ++ HashTable *props = zend_get_std_object_handlers()->get_properties(object); + char *ver_str, *url_str = NULL; + size_t ver_len, url_len = 0; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - INIT_PZVAL_ARRAY(&array, props); ++ *is_temp = 0; + -#define ASSOC_PROP(ptype, n, val) \ - do { \ - zend_property_info *pi; \ - if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \ - add_assoc_ ##ptype## _ex(&array, pi->name, pi->name_length + 1, val); \ - } \ - } while(0) \ - -#define ASSOC_STRING(name, val) ASSOC_STRINGL(name, val, strlen(val)) -#define ASSOC_STRINGL(name, val, len) ASSOC_STRINGL_EX(name, val, len, 1) -#define ASSOC_STRINGL_EX(n, val, len, cpy) \ ++#define UPDATE_PROP(name_str, action_with_tmp) \ + do { \ + zend_property_info *pi; \ - if (SUCCESS == zend_hash_find(&obj->zo.ce->properties_info, n, sizeof(n), (void *) &pi)) { \ - add_assoc_stringl_ex(&array, pi->name, pi->name_length + 1, val, len, cpy); \ ++ if ((pi = zend_hash_str_find_ptr(&obj->zo.ce->properties_info, name_str, lenof(name_str)))) { \ ++ action_with_tmp; \ ++ zend_hash_update_ind(props, pi->name, &tmp); \ + } \ + } while(0) + - ASSOC_PROP(long, "type", obj->message->type); ++ UPDATE_PROP("type", ZVAL_LONG(&tmp, obj->message->type)); ++ + ver_len = spprintf(&ver_str, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor); - ASSOC_STRINGL_EX("httpVersion", ver_str, ver_len, 0); ++ UPDATE_PROP("httpVersion", ZVAL_STR(&tmp, php_http_cs2zs(ver_str, ver_len))); + + switch (obj->message->type) { + case PHP_HTTP_REQUEST: - ASSOC_PROP(long, "responseCode", 0); - ASSOC_STRINGL("responseStatus", "", 0); - ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method)); ++ UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, 0)); ++ UPDATE_PROP("responseStatus", ZVAL_EMPTY_STRING(&tmp)); ++ UPDATE_PROP("requestMethod", ZVAL_STRING(&tmp, STR_PTR(obj->message->http.info.request.method))); + if (obj->message->http.info.request.url) { + php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0); - ASSOC_STRINGL_EX("requestUrl", url_str, url_len, 0); ++ UPDATE_PROP("requestUrl", ZVAL_STR(&tmp, php_http_cs2zs(url_str, url_len))); + } else { - ASSOC_STRINGL("requestUrl", "", 0); ++ UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp)); + } + + break; + + case PHP_HTTP_RESPONSE: - ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code); - ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status)); - ASSOC_STRINGL("requestMethod", "", 0); - ASSOC_STRINGL("requestUrl", "", 0); ++ UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, obj->message->http.info.response.code)); ++ UPDATE_PROP("responseStatus", ZVAL_STRING(&tmp, STR_PTR(obj->message->http.info.response.status))); ++ UPDATE_PROP("requestMethod", ZVAL_EMPTY_STRING(&tmp)); ++ UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp)); + break; + + case PHP_HTTP_NONE: + default: - ASSOC_PROP(long, "responseCode", 0); - ASSOC_STRINGL("responseStatus", "", 0); - ASSOC_STRINGL("requestMethod", "", 0); - ASSOC_STRINGL("requestUrl", "", 0); ++ UPDATE_PROP("responseCode", ZVAL_LONG(&tmp, 0)); ++ UPDATE_PROP("responseStatus", ZVAL_EMPTY_STRING(&tmp)); ++ UPDATE_PROP("requestMethod", ZVAL_EMPTY_STRING(&tmp)); ++ UPDATE_PROP("requestUrl", ZVAL_EMPTY_STRING(&tmp)); + break; + } + - MAKE_STD_ZVAL(headers); - array_init(headers); - zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - ASSOC_PROP(zval, "headers", headers); ++ UPDATE_PROP("headers", ++ array_init(&tmp); ++ array_copy(&obj->message->hdrs, Z_ARRVAL(tmp)); ++ ); + - MAKE_STD_ZVAL(body); - if (obj->body) { - ZVAL_OBJVAL(body, obj->body->zv, 1); - } else { - ZVAL_NULL(body); - } - ASSOC_PROP(zval, "body", body); ++ UPDATE_PROP("body", ++ if (obj->body) { ++ ZVAL_OBJECT(&tmp, &obj->body->zo, 1); ++ } else { ++ ZVAL_NULL(&tmp); ++ } ++ ); + - MAKE_STD_ZVAL(parent); - if (obj->message->parent) { - ZVAL_OBJVAL(parent, obj->parent->zv, 1); - } else { - ZVAL_NULL(parent); - } - ASSOC_PROP(zval, "parentMessage", parent); ++ UPDATE_PROP("parentMessage", ++ if (obj->message->parent) { ++ ZVAL_OBJECT(&tmp, &obj->parent->zo, 1); ++ } else { ++ ZVAL_NULL(&tmp); ++ } ++ ); + + return props; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___construct, 0, 0, 0) + ZEND_ARG_INFO(0, message) + ZEND_ARG_INFO(0, greedy) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, __construct) + { + zend_bool greedy = 1; + zval *zmessage = NULL; + php_http_message_t *msg = NULL; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!b", &zmessage, &greedy), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh); + if (zmessage && Z_TYPE_P(zmessage) == IS_RESOURCE) { + php_stream *s; + php_http_message_parser_t p; + zend_error_handling zeh; + - zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); - php_stream_from_zval(s, &zmessage); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh); ++ php_stream_from_zval(s, zmessage); ++ zend_restore_error_handling(&zeh); + - if (s && php_http_message_parser_init(&p TSRMLS_CC)) { ++ if (s && php_http_message_parser_init(&p)) { + unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0); + php_http_buffer_t buf; + + php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC); + if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) { + if (!EG(exception)) { + php_http_throw(bad_message, "Could not parse message from stream", NULL); + } + } + php_http_buffer_dtor(&buf); + php_http_message_parser_dtor(&p); + } + + if (!msg && !EG(exception)) { + php_http_throw(bad_message, "Empty message received from stream", NULL); + } + } else if (zmessage) { - zmessage = php_http_ztyp(IS_STRING, zmessage); - msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage), greedy TSRMLS_CC); ++ zend_string *zs_msg = zval_get_string(zmessage); ++ ++ msg = php_http_message_parse(NULL, zs_msg->val, zs_msg->len, greedy); + + if (!msg && !EG(exception)) { - php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage)); ++ php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, zs_msg->len), zs_msg->val); + } - zval_ptr_dtor(&zmessage); ++ zend_string_release(zs_msg); + } + + if (msg) { + php_http_message_dtor(obj->message); + obj->message = msg; + if (obj->message->parent) { - php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, &obj->parent TSRMLS_CC); ++ obj->parent = php_http_message_object_new_ex(obj->zo.ce, obj->message->parent); + } + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getBody, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getBody) + { + php_http_message_object_t *obj; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (!obj->body) { + php_http_message_object_init_body_object(obj); + + } + if (obj->body) { - RETVAL_OBJVAL(obj->body->zv, 1); ++ RETVAL_OBJECT(&obj->body->zo, 1); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setBody, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setBody) + { + zval *zbody; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zbody, php_http_message_body_class_entry)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zbody, php_http_message_body_class_entry)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - php_http_message_object_prophandler_set_body(obj, zbody TSRMLS_CC); ++ php_http_message_object_prophandler_set_body(obj, zbody); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addBody, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, body, http\\Message\\Body, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, addBody) + { + zval *new_body; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &new_body, php_http_message_body_class_entry)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - php_http_message_body_object_t *new_obj = zend_object_store_get_object(new_body TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &new_body, php_http_message_body_class_entry)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); ++ php_http_message_body_object_t *new_obj = PHP_HTTP_OBJ(NULL, new_body); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + php_http_message_body_to_callback(new_obj->body, (php_http_pass_callback_t) php_http_message_body_append, obj->message->body, 0, 0); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeader, 0, 0, 1) + ZEND_ARG_INFO(0, header) + ZEND_ARG_INFO(0, into_class) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getHeader) + { + char *header_str; - int header_len; ++ size_t header_len; + zend_class_entry *header_ce = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C!", &header_str, &header_len, &header_ce)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|C!", &header_str, &header_len, &header_ce)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + zval *header; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) { ++ if ((header = php_http_message_header(obj->message, header_str, header_len))) { + if (!header_ce) { - RETURN_ZVAL(header, 1, 1); - } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) { ++ RETURN_ZVAL(header, 1, 0); ++ } else if (instanceof_function(header_ce, php_http_header_class_entry)) { + php_http_object_method_t cb; - zval *header_name, **argv[2]; - - MAKE_STD_ZVAL(header_name); - ZVAL_STRINGL(header_name, header_str, header_len, 1); ++ zval argv[2]; + - argv[0] = &header_name; - argv[1] = &header; ++ ZVAL_STRINGL(&argv[0], header_str, header_len); ++ ZVAL_COPY(&argv[1], header); + + object_init_ex(return_value, header_ce); - php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC); - php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC); ++ php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct")); ++ php_http_object_method_call(&cb, return_value, NULL, 2, argv); + php_http_object_method_dtor(&cb); + - zval_ptr_dtor(&header_name); - zval_ptr_dtor(&header); ++ zval_ptr_dtor(&argv[0]); ++ zval_ptr_dtor(&argv[1]); + + return; + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name); ++ php_error_docref(NULL, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name->val); + } + } + } + RETURN_FALSE; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHeaders, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getHeaders) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + array_init(return_value); + array_copy(&obj->message->hdrs, Z_ARRVAL_P(return_value)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeader, 0, 0, 1) + ZEND_ARG_INFO(0, header) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setHeader) + { + zval *zvalue = NULL; + char *name_str; - int name_len; ++ size_t name_len; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &name_str, &name_len, &zvalue)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z!", &name_str, &name_len, &zvalue)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (!zvalue) { - zend_symtable_del(&obj->message->hdrs, name, name_len + 1); ++ zend_symtable_str_del(&obj->message->hdrs, name, name_len); + } else { - Z_ADDREF_P(zvalue); - zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); ++ Z_TRY_ADDREF_P(zvalue); ++ zend_symtable_str_update(&obj->message->hdrs, name, name_len, zvalue); + } + efree(name); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHeaders, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, headers, 1) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setHeaders) + { + zval *new_headers = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!", &new_headers)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "a/!", &new_headers)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + zend_hash_clean(&obj->message->hdrs); + if (new_headers) { + array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, 0, ARRAY_JOIN_PRETTIFY|ARRAY_JOIN_STRONLY); + } + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeader, 0, 0, 2) + ZEND_ARG_INFO(0, header) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, addHeader) + { + zval *zvalue; + char *name_str; - int name_len; ++ size_t name_len; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &zvalue)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name_str, &name_len, &zvalue)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *name = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); + zval *header; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - Z_ADDREF_P(zvalue); - if ((header = php_http_message_header(obj->message, name, name_len, 0))) { ++ Z_TRY_ADDREF_P(zvalue); ++ if ((header = php_http_message_header(obj->message, name, name_len))) { + convert_to_array(header); - zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL); - zval_ptr_dtor(&header); ++ zend_hash_next_index_insert(Z_ARRVAL_P(header), zvalue); + } else { - zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); ++ zend_symtable_str_update(&obj->message->hdrs, name, name_len, zvalue); + } + efree(name); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_addHeaders, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, headers, 0) + ZEND_ARG_INFO(0, append) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, addHeaders) + { + zval *new_headers; + zend_bool append = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &new_headers, &append)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &new_headers, &append)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + array_join(Z_ARRVAL_P(new_headers), &obj->message->hdrs, append, ARRAY_JOIN_STRONLY|ARRAY_JOIN_PRETTIFY); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getType, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getType) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + RETURN_LONG(obj->message->type); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setType, 0, 0, 1) + ZEND_ARG_INFO(0, type) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setType) + { - long type; ++ zend_long type; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &type)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &type)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + php_http_message_set_type(obj->message, type); + } + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getInfo, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getInfo) + { + if (SUCCESS == zend_parse_parameters_none()) { - char *tmp = NULL; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ char *str, *tmp = NULL; ++ size_t len; ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + switch (obj->message->type) { + case PHP_HTTP_REQUEST: - Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, "")); ++ len = spprintf(&str, 0, PHP_HTTP_INFO_REQUEST_FMT_ARGS(&obj->message->http, tmp, "")); + PTR_FREE(tmp); + break; + case PHP_HTTP_RESPONSE: - Z_STRLEN_P(return_value) = spprintf(&Z_STRVAL_P(return_value), 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, "")); ++ len = spprintf(&str, 0, PHP_HTTP_INFO_RESPONSE_FMT_ARGS(&obj->message->http, tmp, "")); + PTR_FREE(tmp); + break; + default: + RETURN_NULL(); + break; + } - Z_TYPE_P(return_value) = IS_STRING; - return; ++ ++ RETVAL_STR(php_http_cs2zs(str, len)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setInfo, 0, 0, 1) + ZEND_ARG_INFO(0, http_info) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setInfo) + { + char *str; - int len; ++ size_t len; + php_http_message_object_t *obj; + php_http_info_t inf; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - if (!php_http_info_parse(&inf, str TSRMLS_CC)) { ++ if (!php_http_info_parse(&inf, str)) { + php_http_throw(bad_header, "Could not parse message info '%s'", str); + return; + } + + php_http_message_set_info(obj->message, &inf); + php_http_info_dtor(&inf); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getHttpVersion, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getHttpVersion) + { + if (SUCCESS == zend_parse_parameters_none()) { + char *str; + size_t len; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL TSRMLS_CC); - RETURN_STRINGL(str, len, 0); ++ php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL); ++ RETURN_STR(php_http_cs2zs(str, len)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setHttpVersion, 0, 0, 1) + ZEND_ARG_INFO(0, http_version) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setHttpVersion) + { + char *v_str; - int v_len; ++ size_t v_len; + php_http_version_t version; + php_http_message_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &v_str, &v_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &v_str, &v_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - php_http_expect(php_http_version_parse(&version, v_str TSRMLS_CC), unexpected_val, return); ++ php_http_expect(php_http_version_parse(&version, v_str), unexpected_val, return); + + obj->message->http.version = version; + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseCode, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getResponseCode) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not if type response"); ++ php_error_docref(NULL, E_WARNING, "http\\Message is not if type response"); + RETURN_FALSE; + } + + RETURN_LONG(obj->message->http.info.response.code); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseCode, 0, 0, 1) + ZEND_ARG_INFO(0, response_code) + ZEND_ARG_INFO(0, strict) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setResponseCode) + { - long code; ++ zend_long code; + zend_bool strict = 1; + php_http_message_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|b", &code, &strict), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &code, &strict), invalid_arg, return); + ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { + php_http_throw(bad_method_call, "http\\Message is not of type response", NULL); + return; + } + + if (strict && (code < 100 || code > 599)) { + php_http_throw(invalid_arg, "Invalid response code (100-599): %ld", code); + return; + } + + obj->message->http.info.response.code = code; + PTR_SET(obj->message->http.info.response.status, estrdup(php_http_env_get_response_status_for_code(code))); + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getResponseStatus, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getResponseStatus) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type response"); ++ php_error_docref(NULL, E_WARNING, "http\\Message is not of type response"); + } + + if (obj->message->http.info.response.status) { - RETURN_STRING(obj->message->http.info.response.status, 1); ++ RETURN_STRING(obj->message->http.info.response.status); + } else { + RETURN_EMPTY_STRING(); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setResponseStatus, 0, 0, 1) + ZEND_ARG_INFO(0, response_status) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setResponseStatus) + { + char *status; - int status_len; ++ size_t status_len; + php_http_message_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &status, &status_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &status, &status_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { + php_http_throw(bad_method_call, "http\\Message is not of type response", NULL); + } + + PTR_SET(obj->message->http.info.response.status, estrndup(status, status_len)); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestMethod, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getRequestMethod) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request"); ++ php_error_docref(NULL, E_WARNING, "http\\Message is not of type request"); + RETURN_FALSE; + } + + if (obj->message->http.info.request.method) { - RETURN_STRING(obj->message->http.info.request.method, 1); ++ RETURN_STRING(obj->message->http.info.request.method); + } else { + RETURN_EMPTY_STRING(); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestMethod, 0, 0, 1) + ZEND_ARG_INFO(0, request_method) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setRequestMethod) + { + char *method; - int method_len; ++ size_t method_len; + php_http_message_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &method, &method_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &method, &method_len), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { + php_http_throw(bad_method_call, "http\\Message is not of type request", NULL); + return; + } + + if (method_len < 1) { + php_http_throw(invalid_arg, "Cannot set http\\Message's request method to an empty string", NULL); + return; + } + + PTR_SET(obj->message->http.info.request.method, estrndup(method, method_len)); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getRequestUrl, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getRequestUrl) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "http\\Message is not of type request"); ++ php_error_docref(NULL, E_WARNING, "http\\Message is not of type request"); + RETURN_FALSE; + } + + if (obj->message->http.info.request.url) { + char *url_str; + size_t url_len; + + php_http_url_to_string(obj->message->http.info.request.url, &url_str, &url_len, 0); - RETURN_STRINGL(url_str, url_len, 0); ++ RETURN_STR(php_http_cs2zs(url_str, url_len)); + } else { + RETURN_EMPTY_STRING(); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_setRequestUrl, 0, 0, 1) + ZEND_ARG_INFO(0, url) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, setRequestUrl) + { + zval *zurl; + php_http_url_t *url; + php_http_message_object_t *obj; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zurl), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zurl), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { + php_http_throw(bad_method_call, "http\\Message is not of type request", NULL); + return; + } + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); - url = php_http_url_from_zval(zurl, ~0 TSRMLS_CC); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh); ++ url = php_http_url_from_zval(zurl, ~0); ++ zend_restore_error_handling(&zeh); + + if (url && php_http_url_is_empty(url)) { + php_http_url_free(&url); + php_http_throw(invalid_arg, "Cannot set http\\Message's request url to an empty string", NULL); + } else if (url) { + PTR_SET(obj->message->http.info.request.url, url); + } + + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_getParentMessage, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, getParentMessage) + { + php_http_message_object_t *obj; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (!obj->message->parent) { + php_http_throw(unexpected_val, "http\\Message has not parent message", NULL); + return; + } + - RETVAL_OBJVAL(obj->parent->zv, 1); ++ RETVAL_OBJECT(&obj->parent->zo, 1); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage___toString, 0, 0, 0) + ZEND_END_ARG_INFO(); + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toString, 0, 0, 0) + ZEND_ARG_INFO(0, include_parent) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, toString) + { + zend_bool include_parent = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &include_parent)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &include_parent)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *string; + size_t length; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (include_parent) { + php_http_message_serialize(obj->message, &string, &length); + } else { + php_http_message_to_string(obj->message, &string, &length); + } + if (string) { - RETURN_STRINGL(string, length, 0); ++ RETURN_STR(php_http_cs2zs(string, length)); + } + } + RETURN_EMPTY_STRING(); + } + -#ifdef ZTS -static size_t write_to_stream(void *s, const char *str, size_t len) -{ - TSRMLS_FETCH(); - return php_stream_write(s, str, len); -} -#else -# define write_to_stream (php_http_pass_callback_t)_php_stream_write -#endif - + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toStream, 0, 0, 1) + ZEND_ARG_INFO(0, stream) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, toStream) + { + zval *zstream; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zstream)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zstream)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + php_stream *s; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - php_stream_from_zval(s, &zstream); - php_http_message_to_callback(obj->message, write_to_stream, s); ++ php_stream_from_zval(s, zstream); ++ php_http_message_to_callback(obj->message, (php_http_pass_callback_t) _php_stream_write, s); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_toCallback, 0, 0, 1) + ZEND_ARG_INFO(0, callback) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, toCallback) + { + php_http_pass_fcall_arg_t fcd; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", &fcd.fci, &fcd.fcc)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "f", &fcd.fci, &fcd.fcc)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - fcd.fcz = getThis(); - Z_ADDREF_P(fcd.fcz); - TSRMLS_SET_CTX(fcd.ts); - ++ ZVAL_COPY(&fcd.fcz, getThis()); + php_http_message_to_callback(obj->message, php_http_pass_fcall_callback, &fcd); + zend_fcall_info_args_clear(&fcd.fci, 1); - + zval_ptr_dtor(&fcd.fcz); ++ + RETURN_ZVAL(getThis(), 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_serialize, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, serialize) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *string; + size_t length; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + php_http_message_serialize(obj->message, &string, &length); - RETURN_STRINGL(string, length, 0); ++ RETURN_STR(php_http_cs2zs(string, length)); + } + RETURN_EMPTY_STRING(); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_unserialize, 0, 0, 1) + ZEND_ARG_INFO(0, serialized) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, unserialize) + { - int length; ++ size_t length; + char *serialized; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &length)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &serialized, &length)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + php_http_message_t *msg; + + if (obj->message) { ++ /* do not free recursively */ + php_http_message_dtor(obj->message); + efree(obj->message); + } - if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) { ++ if ((msg = php_http_message_parse(NULL, serialized, length, 1))) { + obj->message = msg; + } else { - obj->message = php_http_message_init(NULL, 0, NULL TSRMLS_CC); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not unserialize http\\Message"); ++ obj->message = php_http_message_init(NULL, 0, NULL); ++ php_error_docref(NULL, E_ERROR, "Could not unserialize http\\Message"); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_detach, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, detach) + { - php_http_message_object_t *obj; ++ php_http_message_object_t *obj, *new_obj; ++ php_http_message_t *msg_cpy; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - RETVAL_OBJVAL(php_http_message_object_new_ex(obj->zo.ce, php_http_message_copy_ex(obj->message, NULL, 0), NULL TSRMLS_CC), 0); ++ msg_cpy = php_http_message_copy_ex(obj->message, NULL, 0); ++ new_obj = php_http_message_object_new_ex(obj->zo.ce, msg_cpy); ++ ++ RETVAL_OBJ(&new_obj->zo); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_prepend, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0) + ZEND_ARG_INFO(0, top) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, prepend) + { + zval *prepend; + zend_bool top = 1; + php_http_message_t *msg[2]; + php_http_message_object_t *obj, *prepend_obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &prepend, php_http_message_class_entry, &top), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O|b", &prepend, php_http_message_class_entry, &top), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC); ++ prepend_obj = PHP_HTTP_OBJ(NULL, prepend); + PHP_HTTP_MESSAGE_OBJECT_INIT(prepend_obj); + + /* safety check */ + for (msg[0] = obj->message; msg[0]; msg[0] = msg[0]->parent) { + for (msg[1] = prepend_obj->message; msg[1]; msg[1] = msg[1]->parent) { + if (msg[0] == msg[1]) { + php_http_throw(unexpected_val, "Cannot prepend a message located within the same message chain", NULL); + return; + } + } + } + - php_http_message_object_prepend(getThis(), prepend, top TSRMLS_CC); ++ php_http_message_object_prepend(getThis(), prepend, top); + RETURN_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_reverse, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, reverse) + { + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - php_http_message_object_reverse(getThis(), return_value TSRMLS_CC); ++ php_http_message_object_reverse(getThis(), return_value); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_isMultipart, 0, 0, 0) + ZEND_ARG_INFO(1, boundary) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, isMultipart) + { + zval *zboundary = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &zboundary)) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!", &zboundary)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *boundary = NULL; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL)); ++ if (php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL)) { ++ RETVAL_TRUE; ++ } else { ++ RETVAL_FALSE; ++ } + + if (zboundary && boundary) { ++ ZVAL_DEREF(zboundary); + zval_dtor(zboundary); - ZVAL_STRING(zboundary, boundary, 0); ++ ZVAL_STR(zboundary, php_http_cs2zs(boundary, strlen(boundary))); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_splitMultipartBody, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, splitMultipartBody) + { + php_http_message_object_t *obj; + php_http_message_t *msg; + char *boundary = NULL; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (!php_http_message_is_multipart(obj->message, &boundary)) { + php_http_throw(bad_method_call, "http\\Message is not a multipart message", NULL); + return; + } + + php_http_expect(msg = php_http_message_body_split(obj->message->body, boundary), bad_message, return); + + PTR_FREE(boundary); + - RETURN_OBJVAL(php_http_message_object_new_ex(php_http_message_class_entry, msg, NULL TSRMLS_CC), 0); ++ RETURN_OBJ(&php_http_message_object_new_ex(obj->zo.ce, msg)->zo); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_count, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, count) + { - long count_mode = -1; ++ zend_long count_mode = -1; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &count_mode)) { - long i = 0; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &count_mode)) { ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + - php_http_message_count(i, obj->message); - RETURN_LONG(i); ++ RETURN_LONG(php_http_message_count(obj->message)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_rewind, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, rewind) + { + if (SUCCESS == zend_parse_parameters_none()) { + zval *zobj = getThis(); - php_http_message_object_t *obj = zend_object_store_get_object(zobj TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + - if (obj->iterator) { ++ if (!Z_ISUNDEF(obj->iterator)) { + zval_ptr_dtor(&obj->iterator); + } - Z_ADDREF_P(zobj); - obj->iterator = zobj; ++ ZVAL_COPY(&obj->iterator, zobj); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_valid, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, valid) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + - RETURN_BOOL(obj->iterator != NULL); ++ RETURN_BOOL(!Z_ISUNDEF(obj->iterator)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_next, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, next) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + - if (obj->iterator) { - php_http_message_object_t *itr = zend_object_store_get_object(obj->iterator TSRMLS_CC); ++ if (!Z_ISUNDEF(obj->iterator)) { ++ php_http_message_object_t *itr = PHP_HTTP_OBJ(NULL, &obj->iterator); + - if (itr && itr->parent) { - zval *old = obj->iterator; - MAKE_STD_ZVAL(obj->iterator); - ZVAL_OBJVAL(obj->iterator, itr->parent->zv, 1); - zval_ptr_dtor(&old); ++ if (itr->parent) { ++ zval tmp; ++ ++ ZVAL_OBJECT(&tmp, &itr->parent->zo, 1); ++ zval_ptr_dtor(&obj->iterator); ++ obj->iterator = tmp; + } else { + zval_ptr_dtor(&obj->iterator); - obj->iterator = NULL; ++ ZVAL_UNDEF(&obj->iterator); + } + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_key, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, key) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + - RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0); ++ RETURN_LONG(Z_ISUNDEF(obj->iterator) ? 0 : Z_OBJ_HANDLE(obj->iterator)); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_current, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessage, current) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + - if (obj->iterator) { - RETURN_ZVAL(obj->iterator, 1, 0); ++ if (!Z_ISUNDEF(obj->iterator)) { ++ RETURN_ZVAL(&obj->iterator, 1, 0); + } + } + } + + static zend_function_entry php_http_message_methods[] = { + PHP_ME(HttpMessage, __construct, ai_HttpMessage___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpMessage, getBody, ai_HttpMessage_getBody, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setBody, ai_HttpMessage_setBody, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, addBody, ai_HttpMessage_addBody, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getHeader, ai_HttpMessage_getHeader, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setHeader, ai_HttpMessage_setHeader, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, addHeader, ai_HttpMessage_addHeader, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getHeaders, ai_HttpMessage_getHeaders, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setHeaders, ai_HttpMessage_setHeaders, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, addHeaders, ai_HttpMessage_addHeaders, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getType, ai_HttpMessage_getType, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setType, ai_HttpMessage_setType, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getInfo, ai_HttpMessage_getInfo, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setInfo, ai_HttpMessage_setInfo, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getResponseCode, ai_HttpMessage_getResponseCode, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setResponseCode, ai_HttpMessage_setResponseCode, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getResponseStatus, ai_HttpMessage_getResponseStatus, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setResponseStatus, ai_HttpMessage_setResponseStatus, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getRequestMethod, ai_HttpMessage_getRequestMethod, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setRequestMethod, ai_HttpMessage_setRequestMethod, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getRequestUrl, ai_HttpMessage_getRequestUrl, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setRequestUrl, ai_HttpMessage_setRequestUrl, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getHttpVersion, ai_HttpMessage_getHttpVersion, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, setHttpVersion, ai_HttpMessage_setHttpVersion, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, getParentMessage, ai_HttpMessage_getParentMessage, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, toString, ai_HttpMessage_toString, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, toCallback, ai_HttpMessage_toCallback, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, toStream, ai_HttpMessage_toStream, ZEND_ACC_PUBLIC) + + /* implements Countable */ + PHP_ME(HttpMessage, count, ai_HttpMessage_count, ZEND_ACC_PUBLIC) + + /* implements Serializable */ + PHP_ME(HttpMessage, serialize, ai_HttpMessage_serialize, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, unserialize, ai_HttpMessage_unserialize, ZEND_ACC_PUBLIC) + + /* implements Iterator */ + PHP_ME(HttpMessage, rewind, ai_HttpMessage_rewind, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, valid, ai_HttpMessage_valid, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, current, ai_HttpMessage_current, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, key, ai_HttpMessage_key, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, next, ai_HttpMessage_next, ZEND_ACC_PUBLIC) + + ZEND_MALIAS(HttpMessage, __toString, toString, ai_HttpMessage___toString, ZEND_ACC_PUBLIC) + + PHP_ME(HttpMessage, detach, ai_HttpMessage_detach, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, prepend, ai_HttpMessage_prepend, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, reverse, ai_HttpMessage_reverse, ZEND_ACC_PUBLIC) + + PHP_ME(HttpMessage, isMultipart, ai_HttpMessage_isMultipart, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessage, splitMultipartBody, ai_HttpMessage_splitMultipartBody, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_message_class_entry; + + PHP_MINIT_FUNCTION(http_message) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Message", php_http_message_methods); - php_http_message_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_message_class_entry = zend_register_internal_class(&ce); + php_http_message_class_entry->create_object = php_http_message_object_new; + memcpy(&php_http_message_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ++ php_http_message_object_handlers.offset = XtOffsetOf(php_http_message_object_t, zo); + php_http_message_object_handlers.clone_obj = php_http_message_object_clone; ++ php_http_message_object_handlers.free_obj = php_http_message_object_free; + php_http_message_object_handlers.read_property = php_http_message_object_read_prop; + php_http_message_object_handlers.write_property = php_http_message_object_write_prop; - php_http_message_object_handlers.get_properties = php_http_message_object_get_props; ++ php_http_message_object_handlers.get_debug_info = php_http_message_object_get_debug_info; + php_http_message_object_handlers.get_property_ptr_ptr = NULL; + - zend_class_implements(php_http_message_class_entry TSRMLS_CC, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator); ++ zend_class_implements(php_http_message_class_entry, 3, spl_ce_Countable, zend_ce_serializable, zend_ce_iterator); + - zend_hash_init(&php_http_message_object_prophandlers, 9, NULL, NULL, 1); - zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("type"), PHP_HTTP_NONE, ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_hash_init(&php_http_message_object_prophandlers, 9, NULL, php_http_message_object_prophandler_hash_dtor, 1); ++ zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("type"), PHP_HTTP_NONE, ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("type"), php_http_message_object_prophandler_get_type, php_http_message_object_prophandler_set_type); - zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("body"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("body"), ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("body"), php_http_message_object_prophandler_get_body, php_http_message_object_prophandler_set_body); - zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestMethod"), "", ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestMethod"), "", ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("requestMethod"), php_http_message_object_prophandler_get_request_method, php_http_message_object_prophandler_set_request_method); - zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestUrl"), "", ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("requestUrl"), "", ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("requestUrl"), php_http_message_object_prophandler_get_request_url, php_http_message_object_prophandler_set_request_url); - zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("responseStatus"), "", ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_string(php_http_message_class_entry, ZEND_STRL("responseStatus"), "", ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("responseStatus"), php_http_message_object_prophandler_get_response_status, php_http_message_object_prophandler_set_response_status); - zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("responseCode"), 0, ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_long(php_http_message_class_entry, ZEND_STRL("responseCode"), 0, ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("responseCode"), php_http_message_object_prophandler_get_response_code, php_http_message_object_prophandler_set_response_code); - zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("httpVersion"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("httpVersion"), ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("httpVersion"), php_http_message_object_prophandler_get_http_version, php_http_message_object_prophandler_set_http_version); - zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("headers"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("headers"), ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("headers"), php_http_message_object_prophandler_get_headers, php_http_message_object_prophandler_set_headers); - zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("parentMessage"), ZEND_ACC_PROTECTED TSRMLS_CC); ++ zend_declare_property_null(php_http_message_class_entry, ZEND_STRL("parentMessage"), ZEND_ACC_PROTECTED); + php_http_message_object_add_prophandler(ZEND_STRL("parentMessage"), php_http_message_object_prophandler_get_parent_message, php_http_message_object_prophandler_set_parent_message); + - zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_NONE"), PHP_HTTP_NONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_REQUEST"), PHP_HTTP_REQUEST TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_RESPONSE"), PHP_HTTP_RESPONSE TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_NONE"), PHP_HTTP_NONE); ++ zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_REQUEST"), PHP_HTTP_REQUEST); ++ zend_declare_class_constant_long(php_http_message_class_entry, ZEND_STRL("TYPE_RESPONSE"), PHP_HTTP_RESPONSE); + + return SUCCESS; + } + + PHP_MSHUTDOWN_FUNCTION(http_message) + { + zend_hash_destroy(&php_http_message_object_prophandlers); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_message.h index 0000000,780ea68..2f99507 mode 000000,100644..100644 --- a/src/php_http_message.h +++ b/src/php_http_message.h @@@ -1,0 -1,103 +1,120 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_MESSAGE_H + #define PHP_HTTP_MESSAGE_H + + #include "php_http_message_body.h" ++#include "php_http_header.h" + + /* required minimum length of an HTTP message "HTTP/1.1" */ + #define PHP_HTTP_MESSAGE_MIN_SIZE 8 + #define PHP_HTTP_MESSAGE_TYPE(TYPE, msg) ((msg) && ((msg)->type == PHP_HTTP_ ##TYPE)) + + typedef php_http_info_type_t php_http_message_type_t; + typedef struct php_http_message php_http_message_t; + + struct php_http_message { + PHP_HTTP_INFO_IMPL(http, type) + HashTable hdrs; + php_http_message_body_t *body; + php_http_message_t *parent; + void *opaque; -#ifdef ZTS - void ***ts; -#endif + }; + -PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info TSRMLS_DC); ++PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **message, HashTable **headers, php_http_info_t *info); + -PHP_HTTP_API php_http_message_t *php_http_message_init(php_http_message_t *m, php_http_message_type_t t, php_http_message_body_t *body TSRMLS_DC); -PHP_HTTP_API php_http_message_t *php_http_message_init_env(php_http_message_t *m, php_http_message_type_t t TSRMLS_DC); -PHP_HTTP_API php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to); ++PHP_HTTP_API php_http_message_t *php_http_message_init(php_http_message_t *m, php_http_message_type_t t, php_http_message_body_t *body); ++PHP_HTTP_API php_http_message_t *php_http_message_init_env(php_http_message_t *m, php_http_message_type_t t); + PHP_HTTP_API php_http_message_t *php_http_message_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents); ++static inline php_http_message_t *php_http_message_copy(php_http_message_t *from, php_http_message_t *to) ++{ ++ return php_http_message_copy_ex(from, to, 1); ++} ++ + PHP_HTTP_API void php_http_message_dtor(php_http_message_t *message); + PHP_HTTP_API void php_http_message_free(php_http_message_t **message); + + PHP_HTTP_API void php_http_message_set_type(php_http_message_t *m, php_http_message_type_t t); + PHP_HTTP_API void php_http_message_set_info(php_http_message_t *message, php_http_info_t *info); + + PHP_HTTP_API void php_http_message_update_headers(php_http_message_t *msg); + -PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len, int join); ++PHP_HTTP_API zval *php_http_message_header(php_http_message_t *msg, const char *key_str, size_t key_len); ++ ++static inline zend_string *php_http_message_header_string(php_http_message_t *msg, const char *key_str, size_t key_len) ++{ ++ zval *header; ++ ++ if ((header = php_http_message_header(msg, key_str, key_len))) { ++ return php_http_header_value_to_string(header); ++ } ++ return NULL; ++} ++ + PHP_HTTP_API zend_bool php_http_message_is_multipart(php_http_message_t *msg, char **boundary); + + PHP_HTTP_API void php_http_message_to_string(php_http_message_t *msg, char **string, size_t *length); + PHP_HTTP_API void php_http_message_to_struct(php_http_message_t *msg, zval *strct); + PHP_HTTP_API void php_http_message_to_callback(php_http_message_t *msg, php_http_pass_callback_t cb, void *cb_arg); + + PHP_HTTP_API void php_http_message_serialize(php_http_message_t *message, char **string, size_t *length); + PHP_HTTP_API php_http_message_t *php_http_message_reverse(php_http_message_t *msg); + PHP_HTTP_API php_http_message_t *php_http_message_zip(php_http_message_t *one, php_http_message_t *two); + -#define php_http_message_count(c, m) \ -{ \ - php_http_message_t *__tmp_msg = (m); \ - for (c = 0; __tmp_msg; __tmp_msg = __tmp_msg->parent, ++(c)); \ ++static inline size_t php_http_message_count(php_http_message_t *m) ++{ ++ size_t c = 1; ++ ++ while ((m = m->parent)) { ++ ++c; ++ } ++ ++ return c; + } + -PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC); ++PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy); + + typedef struct php_http_message_object { - zend_object zo; - zend_object_value zv; + php_http_message_t *message; + struct php_http_message_object *parent; + php_http_message_body_object_t *body; - zval *iterator; ++ zval iterator; ++ zend_object zo; + } php_http_message_object_t; + + PHP_HTTP_API zend_class_entry *php_http_message_class_entry; + + PHP_MINIT_FUNCTION(http_message); + PHP_MSHUTDOWN_FUNCTION(http_message); + -void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top /* = 1 */ TSRMLS_DC); -void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC); -ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *obj, zval *zbody TSRMLS_DC); ++void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top /* = 1 */); ++void php_http_message_object_reverse(zval *this_ptr, zval *return_value); ++ZEND_RESULT_CODE php_http_message_object_set_body(php_http_message_object_t *obj, zval *zbody); + ZEND_RESULT_CODE php_http_message_object_init_body_object(php_http_message_object_t *obj); + -zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg, php_http_message_object_t **ptr TSRMLS_DC); -zend_object_value php_http_message_object_clone(zval *object TSRMLS_DC); -void php_http_message_object_free(void *object TSRMLS_DC); ++zend_object *php_http_message_object_new(zend_class_entry *ce); ++php_http_message_object_t *php_http_message_object_new_ex(zend_class_entry *ce, php_http_message_t *msg); ++zend_object *php_http_message_object_clone(zval *object); ++void php_http_message_object_free(zend_object *object); + + #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 --cc src/php_http_message_body.c index 0000000,c80c238..d629ae5 mode 000000,100644..100644 --- a/src/php_http_message_body.c +++ b/src/php_http_message_body.c @@@ -1,0 -1,934 +1,902 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + + #define BOUNDARY_OPEN(body) \ + do {\ + size_t size = php_http_message_body_size(body); \ + if (size) { \ + php_stream_truncate_set_size(php_http_message_body_stream(body), size - lenof("--" PHP_HTTP_CRLF)); \ + php_http_message_body_append(body, ZEND_STRL(PHP_HTTP_CRLF)); \ + } else { \ + php_http_message_body_appendf(body, "--%s" PHP_HTTP_CRLF, php_http_message_body_boundary(body)); \ + } \ + } while(0) + + #define BOUNDARY_CLOSE(body) \ + php_http_message_body_appendf(body, PHP_HTTP_CRLF "--%s--" PHP_HTTP_CRLF, php_http_message_body_boundary(body)) + -static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value); -static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value); ++static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, HashTable *fields); ++static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, HashTable *files); + -php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream TSRMLS_DC) ++php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body_ptr, php_stream *stream) + { + php_http_message_body_t *body; + + if (body_ptr && *body_ptr) { + body = *body_ptr; - ++body->refcount; ++ php_http_message_body_addref(body); + return body; + } + + body = ecalloc(1, sizeof(php_http_message_body_t)); + body->refcount = 1; + + if (stream) { - php_stream_auto_cleanup(stream); - body->stream_id = php_stream_get_resource_id(stream); - zend_list_addref(body->stream_id); ++ body->res = stream->res; ++ ++GC_REFCOUNT(body->res); + } else { + stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff); - php_stream_auto_cleanup(stream); - body->stream_id = php_stream_get_resource_id(stream); ++ body->res = stream->res; + } - TSRMLS_SET_CTX(body->ts); ++ php_stream_auto_cleanup(stream); + + if (body_ptr) { + *body_ptr = body; + } + + return body; + } + + unsigned php_http_message_body_addref(php_http_message_body_t *body) + { + return ++body->refcount; + } + + php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to) + { + if (from) { - TSRMLS_FETCH_FROM_CTX(from->ts); - + if (to) { + php_stream_truncate_set_size(php_http_message_body_stream(to), 0); + } else { - to = php_http_message_body_init(NULL, NULL TSRMLS_CC); ++ to = php_http_message_body_init(NULL, NULL); + } + php_http_message_body_to_stream(from, php_http_message_body_stream(to), 0, 0); + + if (to->boundary) { + efree(to->boundary); + } + if (from->boundary) { + to->boundary = estrdup(from->boundary); + } + } else { + to = NULL; + } + return to; + } + + void php_http_message_body_free(php_http_message_body_t **body_ptr) + { + if (*body_ptr) { + php_http_message_body_t *body = *body_ptr; + + if (!--body->refcount) { - TSRMLS_FETCH_FROM_CTX(body->ts); - /* NOFIXME: shows leakinfo in DEBUG mode */ - zend_list_delete(body->stream_id); ++ zend_list_delete(body->res); + PTR_FREE(body->boundary); + efree(body); + } + *body_ptr = NULL; + } + } + + const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body) + { - TSRMLS_FETCH_FROM_CTX(body->ts); + php_stream_stat(php_http_message_body_stream(body), &body->ssb); + return &body->ssb; + } + + const char *php_http_message_body_boundary(php_http_message_body_t *body) + { + if (!body->boundary) { + union { double dbl; int num[2]; } data; - TSRMLS_FETCH_FROM_CTX(body->ts); + - data.dbl = php_combined_lcg(TSRMLS_C); ++ data.dbl = php_combined_lcg(); + spprintf(&body->boundary, 0, "%x.%x", data.num[0], data.num[1]); + } + return body->boundary; + } + + char *php_http_message_body_etag(php_http_message_body_t *body) + { + php_http_etag_t *etag; + php_stream *s = php_http_message_body_stream(body); - TSRMLS_FETCH_FROM_CTX(body->ts); + + /* real file or temp buffer ? */ + if (s->ops != &php_stream_temp_ops && s->ops != &php_stream_memory_ops) { + php_stream_stat(php_http_message_body_stream(body), &body->ssb); + + if (body->ssb.sb.st_mtime) { + char *etag; + + spprintf(&etag, 0, "%lx-%lx-%lx", body->ssb.sb.st_ino, body->ssb.sb.st_mtime, body->ssb.sb.st_size); + return etag; + } + } + + /* content based */ - if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode TSRMLS_CC))) { ++ if ((etag = php_http_etag_init(PHP_HTTP_G->env.etag_mode))) { + php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_etag_update, etag, 0, 0); + return php_http_etag_finish(etag); + } + + return NULL; + } + -void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen) ++zend_string *php_http_message_body_to_string(php_http_message_body_t *body, off_t offset, size_t forlen) + { + php_stream *s = php_http_message_body_stream(body); - TSRMLS_FETCH_FROM_CTX(body->ts); + + php_stream_seek(s, offset, SEEK_SET); + if (!forlen) { + forlen = -1; + } - *len = php_stream_copy_to_mem(s, buf, forlen, 0); ++ return php_stream_copy_to_mem(s, forlen, 0); + } + + ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *dst, off_t offset, size_t forlen) + { + php_stream *s = php_http_message_body_stream(body); - TSRMLS_FETCH_FROM_CTX(body->ts); + + php_stream_seek(s, offset, SEEK_SET); + + if (!forlen) { + forlen = -1; + } + return php_stream_copy_to_stream_ex(s, dst, forlen, NULL); + } + + ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen) + { + php_stream *s = php_http_message_body_stream(body); + char *buf = emalloc(0x1000); - TSRMLS_FETCH_FROM_CTX(body->ts); + + php_stream_seek(s, offset, SEEK_SET); + + if (!forlen) { + forlen = -1; + } + while (!php_stream_eof(s)) { + size_t read = php_stream_read(s, buf, MIN(forlen, 0x1000)); + + if (read) { + if (-1 == cb(cb_arg, buf, read)) { + return FAILURE; + } + } + + if (read < MIN(forlen, sizeof(buf))) { + break; + } + + if (forlen && !(forlen -= read)) { + break; + } + } + efree(buf); + + return SUCCESS; + } + + size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len) + { + php_stream *s; + size_t written; - TSRMLS_FETCH_FROM_CTX(body->ts); + + if (!(s = php_http_message_body_stream(body))) { + return -1; + } + + if (s->ops->seek) { + php_stream_seek(s, 0, SEEK_END); + } + + written = php_stream_write(s, buf, len); + + if (written != len) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written); ++ php_error_docref(NULL, E_WARNING, "Failed to append %zu bytes to body; wrote %zu", len, written); + } + + return len; + } + + size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...) + { + va_list argv; + char *print_str; + size_t print_len; + + va_start(argv, fmt); + print_len = vspprintf(&print_str, 0, fmt, argv); + va_end(argv); + + print_len = php_http_message_body_append(body, print_str, print_len); + efree(print_str); + + return print_len; + } + + ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files) + { - zval tmp; - + if (fields) { - INIT_PZVAL_ARRAY(&tmp, fields); - if (SUCCESS != add_recursive_fields(body, NULL, &tmp)) { ++ if (SUCCESS != add_recursive_fields(body, NULL, fields)) { + return FAILURE; + } + } + if (files) { - INIT_PZVAL_ARRAY(&tmp, files); - if (SUCCESS != add_recursive_files(body, NULL, &tmp)) { ++ if (SUCCESS != add_recursive_files(body, NULL, files)) { + return FAILURE; + } + } + + return SUCCESS; + } + + void php_http_message_body_add_part(php_http_message_body_t *body, php_http_message_t *part) + { - TSRMLS_FETCH_FROM_CTX(body->ts); - + BOUNDARY_OPEN(body); + php_http_message_to_callback(part, (php_http_pass_callback_t) php_http_message_body_append, body); + BOUNDARY_CLOSE(body); + } + + + ZEND_RESULT_CODE php_http_message_body_add_form_field(php_http_message_body_t *body, const char *name, const char *value_str, size_t value_len) + { - char *safe_name; - TSRMLS_FETCH_FROM_CTX(body->ts); ++ zend_string *safe_name = zend_string_init(name, strlen(name), 0); + - safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC); ++ safe_name = php_addslashes(safe_name, 1); + + BOUNDARY_OPEN(body); + php_http_message_body_appendf( + body, + "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF + "" PHP_HTTP_CRLF, - safe_name ++ safe_name->val + ); + php_http_message_body_append(body, value_str, value_len); + BOUNDARY_CLOSE(body); + - efree(safe_name); ++ zend_string_release(safe_name); + return SUCCESS; + } + + ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *path, php_stream *in) + { - char *safe_name, *path_dup = estrdup(path), *bname; - size_t bname_len; - TSRMLS_FETCH_FROM_CTX(body->ts); ++ size_t path_len = strlen(path); ++ char *path_dup = estrndup(path, path_len); ++ zend_string *base_name, *safe_name = zend_string_init(name, strlen(name), 0); + - safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC); - - php_basename(path_dup, strlen(path_dup), NULL, 0, &bname, &bname_len TSRMLS_CC); ++ safe_name = php_addslashes(safe_name, 1); ++ base_name = php_basename(path_dup, path_len, NULL, 0); + + BOUNDARY_OPEN(body); + php_http_message_body_appendf( + body, + "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"" PHP_HTTP_CRLF + "Content-Transfer-Encoding: binary" PHP_HTTP_CRLF + "Content-Type: %s" PHP_HTTP_CRLF + PHP_HTTP_CRLF, - safe_name, bname, ctype ++ safe_name->val, base_name->val, ctype + ); + php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL); + BOUNDARY_CLOSE(body); + - efree(safe_name); ++ zend_string_release(safe_name); ++ zend_string_release(base_name); + efree(path_dup); - efree(bname); + + return SUCCESS; + } + -static inline char *format_key(uint type, char *str, ulong num, const char *prefix) { ++static inline char *format_key(php_http_arrkey_t *key, const char *prefix) { + char *new_key = NULL; + + if (prefix && *prefix) { - if (type == HASH_KEY_IS_STRING) { - spprintf(&new_key, 0, "%s[%s]", prefix, str); ++ if (key->key) { ++ spprintf(&new_key, 0, "%s[%s]", prefix, key->key->val); + } else { - spprintf(&new_key, 0, "%s[%lu]", prefix, num); ++ spprintf(&new_key, 0, "%s[%lu]", prefix, key->h); + } - } else if (type == HASH_KEY_IS_STRING) { - new_key = estrdup(str); ++ } else if (key->key) { ++ new_key = estrdup(key->key->val); + } else { + new_key = estrdup(""); + } + + return new_key; + } + -static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, zval *value) ++static ZEND_RESULT_CODE add_recursive_field_value(php_http_message_body_t *body, const char *name, zval *value) + { - if (Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) { - zval **val; - HashTable *ht; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - TSRMLS_FETCH_FROM_CTX(body->ts); ++ zend_string *zs = zval_get_string(value); ++ ZEND_RESULT_CODE rc = php_http_message_body_add_form_field(body, name, zs->val, zs->len); ++ zend_string_release(zs); ++ return rc; ++} + - ht = HASH_OF(value); - if (!ht->nApplyCount) { - ++ht->nApplyCount; - FOREACH_KEYVAL(pos, value, key, val) { - char *str = format_key(key.type, key.str, key.num, name); - if (SUCCESS != add_recursive_fields(body, str, *val)) { ++static ZEND_RESULT_CODE add_recursive_fields(php_http_message_body_t *body, const char *name, HashTable *fields) ++{ ++ zval *val; ++ php_http_arrkey_t key; ++ ++ if (!ZEND_HASH_GET_APPLY_COUNT(fields)) { ++ ZEND_HASH_INC_APPLY_COUNT(fields); ++ ZEND_HASH_FOREACH_KEY_VAL_IND(fields, key.h, key.key, val) ++ { ++ char *str = format_key(&key, name); ++ ++ if (Z_TYPE_P(val) != IS_ARRAY && Z_TYPE_P(val) != IS_OBJECT) { ++ if (SUCCESS != add_recursive_field_value(body, str, val)) { + efree(str); - ht->nApplyCount--; ++ ZEND_HASH_DEC_APPLY_COUNT(fields); + return FAILURE; + } ++ } else if (SUCCESS != add_recursive_fields(body, str, HASH_OF(val))) { + efree(str); ++ ZEND_HASH_DEC_APPLY_COUNT(fields); ++ return FAILURE; + } - --ht->nApplyCount; ++ efree(str); + } - } else { - zval *cpy = php_http_ztyp(IS_STRING, value); - php_http_message_body_add_form_field(body, name, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)); - zval_ptr_dtor(&cpy); ++ ZEND_HASH_FOREACH_END(); ++ ZEND_HASH_DEC_APPLY_COUNT(fields); + } + + return SUCCESS; + } + -static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value) ++static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, HashTable *files) + { - zval **zdata = NULL, **zfile, **zname, **ztype; - HashTable *ht; - TSRMLS_FETCH_FROM_CTX(body->ts); ++ zval *zdata = NULL, *zfile, *zname, *ztype; + - if (Z_TYPE_P(value) != IS_ARRAY && Z_TYPE_P(value) != IS_OBJECT) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array or object (name, type, file) for message body file to add"); - return FAILURE; - } - - ht = HASH_OF(value); - - if ((SUCCESS != zend_hash_find(ht, ZEND_STRS("name"), (void *) &zname)) - || (SUCCESS != zend_hash_find(ht, ZEND_STRS("type"), (void *) &ztype)) - || (SUCCESS != zend_hash_find(ht, ZEND_STRS("file"), (void *) &zfile)) ++ /* single entry */ ++ if (!(zname = zend_hash_str_find(files, ZEND_STRL("name"))) ++ || !(ztype = zend_hash_str_find(files, ZEND_STRL("type"))) ++ || !(zfile = zend_hash_str_find(files, ZEND_STRL("file"))) + ) { - zval **val; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); ++ zval *val; ++ php_http_arrkey_t key; + - if (!ht->nApplyCount) { - ++ht->nApplyCount; - FOREACH_HASH_KEYVAL(pos, ht, key, val) { - if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) { - char *str = format_key(key.type, key.str, key.num, name); ++ if (!ZEND_HASH_GET_APPLY_COUNT(files)) { ++ ZEND_HASH_INC_APPLY_COUNT(files); ++ ZEND_HASH_FOREACH_KEY_VAL_IND(files, key.h, key.key, val) ++ { ++ if (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT) { ++ char *str = format_key(&key, name); + - if (SUCCESS != add_recursive_files(body, str, *val)) { ++ if (SUCCESS != add_recursive_files(body, str, HASH_OF(val))) { + efree(str); - --ht->nApplyCount; ++ ZEND_HASH_DEC_APPLY_COUNT(files); + return FAILURE; + } + efree(str); + } + } - --ht->nApplyCount; ++ ZEND_HASH_FOREACH_END(); ++ ZEND_HASH_DEC_APPLY_COUNT(files); + } + return SUCCESS; + } else { ++ /* stream entry */ + php_stream *stream; - zval *zfc = php_http_ztyp(IS_STRING, *zfile); ++ zend_string *zfc = zval_get_string(zfile); + - if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) { - if (Z_TYPE_PP(zdata) == IS_RESOURCE) { ++ if ((zdata = zend_hash_str_find(files, ZEND_STRL("data")))) { ++ if (Z_TYPE_P(zdata) == IS_RESOURCE) { + php_stream_from_zval_no_verify(stream, zdata); + } else { - zval *tmp = php_http_ztyp(IS_STRING, *zdata); ++ zend_string *tmp = zval_get_string(zdata); + - stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - zval_ptr_dtor(&tmp); ++ stream = php_stream_memory_open(TEMP_STREAM_READONLY, tmp->val, tmp->len); ++ zend_string_release(tmp); + } + } else { - stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL); ++ stream = php_stream_open_wrapper(zfc->val, "r", REPORT_ERRORS|USE_PATH, NULL); + } + + if (!stream) { - zval_ptr_dtor(&zfc); ++ zend_string_release(zfc); + return FAILURE; + } else { - zval *znc = php_http_ztyp(IS_STRING, *zname), *ztc = php_http_ztyp(IS_STRING, *ztype); - char *key = format_key(HASH_KEY_IS_STRING, Z_STRVAL_P(znc), 0, name); - ZEND_RESULT_CODE ret = php_http_message_body_add_form_file(body, key, Z_STRVAL_P(ztc), Z_STRVAL_P(zfc), stream); ++ zend_string *znc = zval_get_string(zname), *ztc = zval_get_string(ztype); ++ php_http_arrkey_t arrkey = {0, znc}; ++ char *key = format_key(&arrkey, name); ++ ZEND_RESULT_CODE ret = php_http_message_body_add_form_file(body, key, ztc->val, zfc->val, stream); + + efree(key); - zval_ptr_dtor(&znc); - zval_ptr_dtor(&ztc); - zval_ptr_dtor(&zfc); - if (!zdata || Z_TYPE_PP(zdata) != IS_RESOURCE) { ++ zend_string_release(znc); ++ zend_string_release(ztc); ++ zend_string_release(zfc); ++ if (!zdata || Z_TYPE_P(zdata) != IS_RESOURCE) { + php_stream_close(stream); + } + return ret; + } + + } + } + + struct splitbody_arg { + php_http_buffer_t buf; + php_http_message_parser_t *parser; + char *boundary_str; + size_t boundary_len; + size_t consumed; + }; + -static size_t splitbody(void *opaque, char *buf, size_t len TSRMLS_DC) ++static size_t splitbody(void *opaque, char *buf, size_t len) + { + struct splitbody_arg *arg = opaque; + const char *boundary = NULL; + size_t consumed = 0; + int first_boundary; + + do { + first_boundary = !(consumed || arg->consumed); + + if ((boundary = php_http_locate_str(buf, len, arg->boundary_str + first_boundary, arg->boundary_len - first_boundary))) { + size_t real_boundary_len = arg->boundary_len - 1, cut; + const char *real_boundary = boundary + !first_boundary; + int eol_len = 0; + + if (buf + len <= real_boundary + real_boundary_len) { + /* if we just have enough data for the boundary, it's just a byte too less */ + arg->consumed += consumed; + return consumed; + } + + if (!first_boundary) { + /* this is not the first boundary, read rest of this message */ + php_http_buffer_append(&arg->buf, buf, real_boundary - buf); + php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message); + } + + /* move after the boundary */ + cut = real_boundary - buf + real_boundary_len; + buf += cut; + len -= cut; + consumed += cut; + + if (buf == php_http_locate_bin_eol(buf, len, &eol_len)) { + /* skip CRLF */ + buf += eol_len; + len -= eol_len; + consumed += eol_len; + + if (!first_boundary) { + /* advance messages */ + php_http_message_t *msg; + - msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC); ++ msg = php_http_message_init(NULL, 0, NULL); + msg->parent = arg->parser->message; + arg->parser->message = msg; + } + } else { + /* is this the last boundary? */ + if (*buf == '-') { + /* ignore the rest */ + consumed += len; + len = 0; + } else { + /* let this be garbage */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed multipart boundary at pos %zu", consumed); ++ php_error_docref(NULL, E_WARNING, "Malformed multipart boundary at pos %zu", consumed); + return -1; + } + } + } + } while (boundary && len); + + /* let there be room for the next boundary */ + if (len > arg->boundary_len) { + consumed += len - arg->boundary_len; + php_http_buffer_append(&arg->buf, buf, len - arg->boundary_len); + php_http_message_parser_parse(arg->parser, &arg->buf, 0, &arg->parser->message); + } + + arg->consumed += consumed; + return consumed; + } + + php_http_message_t *php_http_message_body_split(php_http_message_body_t *body, const char *boundary) + { + php_stream *s = php_http_message_body_stream(body); + php_http_buffer_t *tmp = NULL; + php_http_message_t *msg = NULL; + struct splitbody_arg arg; - TSRMLS_FETCH_FROM_CTX(body->ts); + + php_http_buffer_init(&arg.buf); - arg.parser = php_http_message_parser_init(NULL TSRMLS_CC); ++ arg.parser = php_http_message_parser_init(NULL); + arg.boundary_len = spprintf(&arg.boundary_str, 0, "\n--%s", boundary); + arg.consumed = 0; + + php_stream_rewind(s); + while (!php_stream_eof(s)) { - php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg TSRMLS_CC); ++ php_http_buffer_passthru(&tmp, 0x1000, (php_http_buffer_pass_func_t) _php_stream_read, s, splitbody, &arg); + } + + msg = arg.parser->message; + arg.parser->message = NULL; + + php_http_buffer_free(&tmp); + php_http_message_parser_free(&arg.parser); + php_http_buffer_dtor(&arg.buf); + PTR_FREE(arg.boundary_str); + + return msg; + } + + static zend_object_handlers php_http_message_body_object_handlers; + -zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_message_body_object_new(zend_class_entry *ce) + { - return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_message_body_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC) ++php_http_message_body_object_t *php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body) + { + php_http_message_body_object_t *o; + - o = ecalloc(1, sizeof(php_http_message_body_object_t)); - zend_object_std_init((zend_object *) o, php_http_message_body_class_entry TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - if (ptr) { - *ptr = o; - } ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, php_http_message_body_class_entry); ++ object_properties_init(&o->zo, ce); + + if (body) { + o->body = body; + } + - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_body_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_message_body_object_handlers; ++ o->zo.handlers = &php_http_message_body_object_handlers; + - return o->zv; ++ return o; + } + -zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC) ++zend_object *php_http_message_body_object_clone(zval *object) + { - zend_object_value new_ov; + php_http_message_body_object_t *new_obj = NULL; - php_http_message_body_object_t *old_obj = zend_object_store_get_object(object TSRMLS_CC); ++ php_http_message_body_object_t *old_obj = PHP_HTTP_OBJ(NULL, object); + php_http_message_body_t *body = php_http_message_body_copy(old_obj->body, NULL); + - new_ov = php_http_message_body_object_new_ex(old_obj->zo.ce, body, &new_obj TSRMLS_CC); - zend_objects_clone_members(&new_obj->zo, new_ov, &old_obj->zo, Z_OBJ_HANDLE_P(object) TSRMLS_CC); ++ new_obj = php_http_message_body_object_new_ex(old_obj->zo.ce, body); ++ zend_objects_clone_members(&new_obj->zo, &old_obj->zo); + - return new_ov; ++ return &new_obj->zo; + } + -void php_http_message_body_object_free(void *object TSRMLS_DC) ++void php_http_message_body_object_free(zend_object *object) + { - php_http_message_body_object_t *obj = object; ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(object, NULL); + + php_http_message_body_free(&obj->body); - zend_object_std_dtor((zend_object *) obj TSRMLS_CC); - efree(obj); ++ zend_object_std_dtor(object); + } + + #define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \ + do { \ + if (!obj->body) { \ - obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \ ++ obj->body = php_http_message_body_init(NULL, NULL); \ + } \ + } while(0) + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___construct, 0, 0, 0) + ZEND_ARG_INFO(0, stream) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, __construct) + { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + zval *zstream = NULL; + php_stream *stream; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &zstream), invalid_arg, return); + + if (zstream) { - php_http_expect(php_stream_from_zval_no_verify(stream, &zstream), unexpected_val, return); ++ php_http_expect(php_stream_from_zval_no_verify(stream, zstream), unexpected_val, return); + + if (obj->body) { + php_http_message_body_free(&obj->body); + } - obj->body = php_http_message_body_init(NULL, stream TSRMLS_CC); ++ obj->body = php_http_message_body_init(NULL, stream); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody___toString, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, __toString) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - char *str; - size_t len; ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); ++ zend_string *zs; + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + - php_http_message_body_to_string(obj->body, &str, &len, 0, 0); - if (str) { - RETURN_STRINGL(str, len, 0); ++ zs = php_http_message_body_to_string(obj->body, 0, 0); ++ if (zs) { ++ RETURN_STR(zs); + } + } + RETURN_EMPTY_STRING(); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_unserialize, 0, 0, 1) + ZEND_ARG_INFO(0, serialized) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, unserialize) + { + char *us_str; - int us_len; ++ size_t us_len; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &us_str, &us_len)) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &us_str, &us_len)) { ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + php_stream *s = php_stream_memory_open(0, us_str, us_len); + - obj->body = php_http_message_body_init(NULL, s TSRMLS_CC); ++ obj->body = php_http_message_body_init(NULL, s); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toStream, 0, 0, 1) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, maxlen) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, toStream) + { + zval *zstream; - long offset = 0, forlen = 0; ++ zend_long offset = 0, forlen = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ll", &zstream, &offset, &forlen)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "r|ll", &zstream, &offset, &forlen)) { + php_stream *stream; - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + - php_stream_from_zval(stream, &zstream); ++ php_stream_from_zval(stream, zstream); + php_http_message_body_to_stream(obj->body, stream, offset, forlen); + RETURN_ZVAL(getThis(), 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_toCallback, 0, 0, 1) + ZEND_ARG_INFO(0, callback) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, maxlen) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, toCallback) + { + php_http_pass_fcall_arg_t fcd; - long offset = 0, forlen = 0; ++ zend_long offset = 0, forlen = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "f|ll", &fcd.fci, &fcd.fcc, &offset, &forlen)) { ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + - fcd.fcz = getThis(); - Z_ADDREF_P(fcd.fcz); - TSRMLS_SET_CTX(fcd.ts); - ++ ZVAL_COPY(&fcd.fcz, getThis()); + php_http_message_body_to_callback(obj->body, php_http_pass_fcall_callback, &fcd, offset, forlen); + zend_fcall_info_args_clear(&fcd.fci, 1); - + zval_ptr_dtor(&fcd.fcz); + RETURN_ZVAL(getThis(), 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getResource, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, getResource) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + - zend_list_addref(obj->body->stream_id); - RETVAL_RESOURCE(obj->body->stream_id); ++ ++GC_REFCOUNT(obj->body->res); ++ RETVAL_RES(obj->body->res); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_getBoundary, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, getBoundary) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_body_object_t * obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_body_object_t * obj = PHP_HTTP_OBJ(NULL, getThis()); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + if (obj->body->boundary) { - RETURN_STRING(obj->body->boundary, 1); ++ RETURN_STRING(obj->body->boundary); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_append, 0, 0, 1) + ZEND_ARG_INFO(0, string) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, append) + { + char *str; - int len; ++ size_t len; + php_http_message_body_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &len), invalid_arg, return); + ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + php_http_expect(len == php_http_message_body_append(obj->body, str, len), runtime, return); + + RETURN_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addForm, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, fields, 1) + ZEND_ARG_ARRAY_INFO(0, files, 1) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, addForm) + { + HashTable *fields = NULL, *files = NULL; + php_http_message_body_object_t *obj; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|h!h!", &fields, &files), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|h!h!", &fields, &files), invalid_arg, return); + ++ obj = PHP_HTTP_OBJ(NULL, getThis()); + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + php_http_expect(SUCCESS == php_http_message_body_add_form(obj->body, fields, files), runtime, return); + + RETURN_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_addPart, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, message, http\\Message, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, addPart) + { + zval *zobj; + php_http_message_body_object_t *obj; + php_http_message_object_t *mobj; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zobj, php_http_message_class_entry), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zobj, php_http_message_class_entry), invalid_arg, return); + - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - mobj = zend_object_store_get_object(zobj TSRMLS_CC); ++ obj = PHP_HTTP_OBJ(NULL, getThis()); ++ mobj = PHP_HTTP_OBJ(NULL, zobj); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh); + php_http_message_body_add_part(obj->body, mobj->message); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + + if (!EG(exception)) { + RETURN_ZVAL(getThis(), 1, 0); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_etag, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, etag) + { + if (SUCCESS == zend_parse_parameters_none()) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + char *etag; + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + if ((etag = php_http_message_body_etag(obj->body))) { - RETURN_STRING(etag, 0); ++ RETURN_STR(php_http_cs2zs(etag, strlen(etag))); + } else { + RETURN_FALSE; + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageBody_stat, 0, 0, 0) + ZEND_ARG_INFO(0, field) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpMessageBody, stat) + { + char *field_str = NULL; - int field_len = 0; ++ size_t field_len = 0; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &field_str, &field_len)) { - php_http_message_body_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &field_str, &field_len)) { ++ php_http_message_body_object_t *obj = PHP_HTTP_OBJ(NULL, getThis()); + const php_stream_statbuf *sb; + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + if ((sb = php_http_message_body_stat(obj->body))) { + if (field_str && field_len) { + switch (*field_str) { + case 's': + case 'S': + RETURN_LONG(sb->sb.st_size); + break; + case 'a': + case 'A': + RETURN_LONG(sb->sb.st_atime); + break; + case 'm': + case 'M': + RETURN_LONG(sb->sb.st_mtime); + break; + case 'c': + case 'C': + RETURN_LONG(sb->sb.st_ctime); + break; + default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str); ++ php_error_docref(NULL, E_WARNING, "Unknown stat field: '%s' (should be one of [s]ize, [a]time, [m]time or [c]time)", field_str); + break; + } + } else { + object_init(return_value); - add_property_long_ex(return_value, ZEND_STRS("size"), sb->sb.st_size TSRMLS_CC); - add_property_long_ex(return_value, ZEND_STRS("atime"), sb->sb.st_atime TSRMLS_CC); - add_property_long_ex(return_value, ZEND_STRS("mtime"), sb->sb.st_mtime TSRMLS_CC); - add_property_long_ex(return_value, ZEND_STRS("ctime"), sb->sb.st_ctime TSRMLS_CC); ++ add_property_long_ex(return_value, ZEND_STRL("size"), sb->sb.st_size); ++ add_property_long_ex(return_value, ZEND_STRL("atime"), sb->sb.st_atime); ++ add_property_long_ex(return_value, ZEND_STRL("mtime"), sb->sb.st_mtime); ++ add_property_long_ex(return_value, ZEND_STRL("ctime"), sb->sb.st_ctime); + } + } + } + } + + static zend_function_entry php_http_message_body_methods[] = { + PHP_ME(HttpMessageBody, __construct, ai_HttpMessageBody___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpMessageBody, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC) + PHP_MALIAS(HttpMessageBody, toString, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC) + PHP_MALIAS(HttpMessageBody, serialize, __toString, ai_HttpMessageBody___toString, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, unserialize, ai_HttpMessageBody_unserialize, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, toStream, ai_HttpMessageBody_toStream, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, toCallback, ai_HttpMessageBody_toCallback, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, getResource, ai_HttpMessageBody_getResource, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, getBoundary, ai_HttpMessageBody_getBoundary, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, append, ai_HttpMessageBody_append, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, addForm, ai_HttpMessageBody_addForm, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, addPart, ai_HttpMessageBody_addPart, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, etag, ai_HttpMessageBody_etag, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageBody, stat, ai_HttpMessageBody_stat, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_message_body_class_entry; + + PHP_MINIT_FUNCTION(http_message_body) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Body", php_http_message_body_methods); - php_http_message_body_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_message_body_class_entry = zend_register_internal_class(&ce); + php_http_message_body_class_entry->create_object = php_http_message_body_object_new; + memcpy(&php_http_message_body_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); ++ php_http_message_body_object_handlers.offset = XtOffsetOf(php_http_message_body_object_t, zo); + php_http_message_body_object_handlers.clone_obj = php_http_message_body_object_clone; - zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 1, zend_ce_serializable); ++ php_http_message_body_object_handlers.free_obj = php_http_message_body_object_free; ++ zend_class_implements(php_http_message_body_class_entry, 1, zend_ce_serializable); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_message_body.h index 0000000,dc2c7a2..92353f7 mode 000000,100644..100644 --- a/src/php_http_message_body.h +++ b/src/php_http_message_body.h @@@ -1,0 -1,78 +1,81 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_MESSAGE_BODY_H + #define PHP_HTTP_MESSAGE_BODY_H + + typedef struct php_http_message_body { - int stream_id; + php_stream_statbuf ssb; ++ zend_resource *res; + char *boundary; + unsigned refcount; -#ifdef ZTS - void ***ts; -#endif + } php_http_message_body_t; + + struct php_http_message; + -PHP_HTTP_API php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body, php_stream *stream TSRMLS_DC); ++PHP_HTTP_API php_http_message_body_t *php_http_message_body_init(php_http_message_body_t **body, php_stream *stream); + PHP_HTTP_API unsigned php_http_message_body_addref(php_http_message_body_t *body); + PHP_HTTP_API php_http_message_body_t *php_http_message_body_copy(php_http_message_body_t *from, php_http_message_body_t *to); + PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form(php_http_message_body_t *body, HashTable *fields, HashTable *files); + PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form_field(php_http_message_body_t *body, const char *name, const char *value_str, size_t value_len); + PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_add_form_file(php_http_message_body_t *body, const char *name, const char *ctype, const char *file, php_stream *stream); + PHP_HTTP_API void php_http_message_body_add_part(php_http_message_body_t *body, struct php_http_message *part); + PHP_HTTP_API size_t php_http_message_body_append(php_http_message_body_t *body, const char *buf, size_t len); + PHP_HTTP_API size_t php_http_message_body_appendf(php_http_message_body_t *body, const char *fmt, ...); -PHP_HTTP_API void php_http_message_body_to_string(php_http_message_body_t *body, char **buf, size_t *len, off_t offset, size_t forlen); ++PHP_HTTP_API zend_string *php_http_message_body_to_string(php_http_message_body_t *body, off_t offset, size_t forlen); + PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_stream(php_http_message_body_t *body, php_stream *s, off_t offset, size_t forlen); + PHP_HTTP_API ZEND_RESULT_CODE php_http_message_body_to_callback(php_http_message_body_t *body, php_http_pass_callback_t cb, void *cb_arg, off_t offset, size_t forlen); + PHP_HTTP_API void php_http_message_body_free(php_http_message_body_t **body); + PHP_HTTP_API const php_stream_statbuf *php_http_message_body_stat(php_http_message_body_t *body); -#define php_http_message_body_size(b) (php_http_message_body_stat((b))->sb.st_size) -#define php_http_message_body_mtime(b) (php_http_message_body_stat((b))->sb.st_mtime) + PHP_HTTP_API char *php_http_message_body_etag(php_http_message_body_t *body); + PHP_HTTP_API const char *php_http_message_body_boundary(php_http_message_body_t *body); + PHP_HTTP_API struct php_http_message *php_http_message_body_split(php_http_message_body_t *body, const char *boundary); + ++static inline size_t php_http_message_body_size(php_http_message_body_t *b) ++{ ++ return php_http_message_body_stat(b)->sb.st_size; ++} ++ ++static inline time_t php_http_message_body_mtime(php_http_message_body_t *b) ++{ ++ return php_http_message_body_stat(b)->sb.st_mtime; ++} ++ + static inline php_stream *php_http_message_body_stream(php_http_message_body_t *body) + { - TSRMLS_FETCH_FROM_CTX(body->ts); - return zend_fetch_resource(NULL TSRMLS_CC, body->stream_id, "stream", NULL, 2, php_file_le_stream(), php_file_le_pstream()); ++ return body->res->ptr; + } + + typedef struct php_http_message_body_object { - zend_object zo; - zend_object_value zv; + php_http_message_body_t *body; ++ zend_object zo; + } php_http_message_body_object_t; + + PHP_HTTP_API zend_class_entry *php_http_message_body_class_entry; + PHP_MINIT_FUNCTION(http_message_body); + -zend_object_value php_http_message_body_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body, php_http_message_body_object_t **ptr TSRMLS_DC); -zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC); -void php_http_message_body_object_free(void *object TSRMLS_DC); ++zend_object *php_http_message_body_object_new(zend_class_entry *ce); ++php_http_message_body_object_t *php_http_message_body_object_new_ex(zend_class_entry *ce, php_http_message_body_t *body); ++zend_object *php_http_message_body_object_clone(zval *object); ++void php_http_message_body_object_free(zend_object *object); + + #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 --cc src/php_http_message_parser.c index 0000000,fae16f1..535a2b3 mode 000000,100644..100644 --- a/src/php_http_message_parser.c +++ b/src/php_http_message_parser.c @@@ -1,0 -1,705 +1,694 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #ifndef DBG_PARSER + # define DBG_PARSER 0 + #endif + + typedef struct php_http_message_parser_state_spec { + php_http_message_parser_state_t state; + unsigned need_data:1; + } php_http_message_parser_state_spec_t; + + static const php_http_message_parser_state_spec_t php_http_message_parser_states[] = { + {PHP_HTTP_MESSAGE_PARSER_STATE_START, 1}, + {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER, 0}, + {PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE, 0}, + {PHP_HTTP_MESSAGE_PARSER_STATE_BODY, 0}, + {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, 1}, + {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, 1}, + {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, 1}, + {PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, 0}, + {PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, 0}, + {PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0} + }; + + #if DBG_PARSER + const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) { + const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"}; + + if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) { + return "FAILURE"; + } + return states[state]; + } + #endif + -php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC) ++php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser) + { + if (!parser) { + parser = emalloc(sizeof(*parser)); + } + memset(parser, 0, sizeof(*parser)); + - TSRMLS_SET_CTX(parser->ts); - - php_http_header_parser_init(&parser->header TSRMLS_CC); ++ php_http_header_parser_init(&parser->header); + + return parser; + } + + php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...) + { + php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; + va_list va_args; + unsigned i; + + if (argc > 0) { + /* short circuit */ + ZEND_PTR_STACK_RESIZE_IF_NEEDED((&parser->stack), argc); + + va_start(va_args, argc); + for (i = 0; i < argc; ++i) { + state = va_arg(va_args, php_http_message_parser_state_t); + zend_ptr_stack_push(&parser->stack, (void *) state); + } + va_end(va_args); + } + + return state; + } + + php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser) + { + if (parser->stack.top) { + return (php_http_message_parser_state_t) parser->stack.elements[parser->stack.top - 1]; + } + return PHP_HTTP_MESSAGE_PARSER_STATE_START; + } + + php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser) + { + if (parser->stack.top) { + return (php_http_message_parser_state_t) zend_ptr_stack_pop(&parser->stack); + } + return PHP_HTTP_MESSAGE_PARSER_STATE_START; + } + + void php_http_message_parser_dtor(php_http_message_parser_t *parser) + { + php_http_header_parser_dtor(&parser->header); + zend_ptr_stack_destroy(&parser->stack); + php_http_message_free(&parser->message); + if (parser->dechunk) { + php_http_encoding_stream_free(&parser->dechunk); + } + if (parser->inflate) { + php_http_encoding_stream_free(&parser->inflate); + } + } + + void php_http_message_parser_free(php_http_message_parser_t **parser) + { + if (*parser) { + php_http_message_parser_dtor(*parser); + efree(*parser); + *parser = NULL; + } + } + + php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message) + { + php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START; - TSRMLS_FETCH_FROM_CTX(parser->ts); + + if (!buf->data) { + php_http_buffer_resize_ex(buf, 0x1000, 1, 0); + } + while (1) { + size_t justread = 0; + #if DBG_PARSER + fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags); + #endif + /* resize if needed */ + if (buf->free < 0x1000) { + php_http_buffer_resize_ex(buf, 0x1000, 1, 0); + } + switch (state) { + case PHP_HTTP_MESSAGE_PARSER_STATE_START: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: + /* read line */ + php_stream_get_line(s, buf->data + buf->used, buf->free, &justread); + /* if we fail reading a whole line, try a single char */ + if (!justread) { + int c = php_stream_getc(s); + + if (c != EOF) { + char s[1] = {c}; + justread = php_http_buffer_append(buf, s, 1); + } + } + php_http_buffer_account(buf, justread); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: + /* read all */ + justread = php_stream_read(s, buf->data + buf->used, buf->free); + php_http_buffer_account(buf, justread); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: + /* read body_length */ + justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length)); + php_http_buffer_account(buf, justread); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED: + /* duh, this is very naive */ + if (parser->body_length) { + justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free)); + + php_http_buffer_account(buf, justread); + + parser->body_length -= justread; + } else { + php_http_buffer_resize(buf, 24); + php_stream_get_line(s, buf->data, buf->free, &justread); + php_http_buffer_account(buf, justread); + + parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16); + } + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY: + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE: + case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL: + /* should not occur */ + abort(); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: + case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE: + return php_http_message_parser_state_is(parser); + } + + if (justread) { + state = php_http_message_parser_parse(parser, buf, flags, message); + } else if (php_stream_eof(s)) { + return php_http_message_parser_parse(parser, buf, flags | PHP_HTTP_MESSAGE_PARSER_CLEANUP, message); + } else { + return state; + } + } + + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; + } + + + php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message) + { + char *str = NULL; + size_t len = 0; + size_t cut = 0; - TSRMLS_FETCH_FROM_CTX(parser->ts); + + while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) { + #if DBG_PARSER + fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", + php_http_message_parser_state_name(php_http_message_parser_state_is(parser)), + flags, + message && *message ? (*message)->type : -1, + buffer->used + ); + _dpf(0, buffer->data, buffer->used); + #endif + + switch (php_http_message_parser_state_pop(parser)) + { + case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE: + return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); + + case PHP_HTTP_MESSAGE_PARSER_STATE_START: + { + char *ptr = buffer->data; + + while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { + ++ptr; + } + + php_http_buffer_cut(buffer, 0, ptr - buffer->data); + + if (buffer->used) { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); + } + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: + { + unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0; + + switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) { + case PHP_HTTP_HEADER_PARSER_STATE_FAILURE: + return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; + + case PHP_HTTP_HEADER_PARSER_STATE_DONE: + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); + break; + + default: + if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) { + return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); + } else { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); + } + } + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: + { - zval *h, *h_loc = NULL, *h_con = NULL, **h_cl = NULL, **h_cr = NULL, **h_te = NULL; ++ zval h, *h_ptr, *h_loc = NULL, *h_con = NULL, *h_ce; ++ zend_bool chunked = 0; ++ zend_long content_length = -1; ++ zend_string *content_range = NULL; + + /* Content-Range has higher precedence than Content-Length, + * and content-length denotes the original length of the entity, + * so let's *NOT* remove CR/CL, because that would fundamentally + * change the meaning of the whole message + */ - if ((h = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding"), 1))) { - zend_hash_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", sizeof("X-Original-Transfer-Encoding"), (void *) &h, sizeof(zval *), (void *) &h_te); - zend_hash_del(&(*message)->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding")); ++ if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding")))) { ++ zend_string *zs = zval_get_string(h_ptr); ++ ++ chunked = zend_string_equals_literal(zs, "chunked"); ++ zend_string_release(zs); ++ ++ Z_TRY_ADDREF_P(h_ptr); ++ zend_hash_str_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), h_ptr); ++ zend_hash_str_del(&(*message)->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding")); + + /* reset */ - MAKE_STD_ZVAL(h); - ZVAL_LONG(h, 0); - zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &h, sizeof(zval *), NULL); - } else if ((h = php_http_message_header(*message, ZEND_STRL("Content-Length"), 1))) { - zend_hash_update(&(*message)->hdrs, "X-Original-Content-Length", sizeof("X-Original-Content-Length"), (void *) &h, sizeof(zval *), (void *) &h_cl); ++ ZVAL_LONG(&h, 0); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &h); ++ } else if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Length")))) { ++ content_length = zval_get_long(h_ptr); ++ Z_TRY_ADDREF_P(h_ptr); ++ zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), h_ptr); + } + - if ((h = php_http_message_header(*message, ZEND_STRL("Content-Range"), 1))) { - zend_hash_find(&(*message)->hdrs, ZEND_STRS("Content-Range"), (void *) &h_cr); - if (h != *h_cr) { - zend_hash_update(&(*message)->hdrs, "Content-Range", sizeof("Content-Range"), &h, sizeof(zval *), (void *) &h_cr); - } else { - zval_ptr_dtor(&h); - } ++ if ((content_range = php_http_message_header_string(*message, ZEND_STRL("Content-Range")))) { ++ ZVAL_STR_COPY(&h, content_range); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Range", lenof("Content-Range"), &h); + } + + /* so, if curl sees a 3xx code, a Location header and a Connection:close header + * it decides not to read the response body. + */ + if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS) + && (*message)->type == PHP_HTTP_RESPONSE + && (*message)->http.info.response.code/100 == 3 - && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"), 1)) - && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"), 1)) ++ && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"))) ++ && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"))) + ) { - if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) { ++ zend_string *con = zval_get_string(h_con); ++ ++ if (php_http_match(con->val, "close", PHP_HTTP_MATCH_WORD)) { ++ zend_string_release(con); + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); - zval_ptr_dtor(&h_loc); - zval_ptr_dtor(&h_con); + break; + } ++ zend_string_release(con); + } - if (h_loc) { - zval_ptr_dtor(&h_loc); - } - if (h_con) { - zval_ptr_dtor(&h_con); - } + - if ((h = php_http_message_header(*message, ZEND_STRL("Content-Encoding"), 1))) { - if (php_http_match(Z_STRVAL_P(h), "gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h), "x-gzip", PHP_HTTP_MATCH_WORD) - || php_http_match(Z_STRVAL_P(h), "deflate", PHP_HTTP_MATCH_WORD) ++ if ((h_ce = php_http_message_header(*message, ZEND_STRL("Content-Encoding")))) { ++ zend_string *ce = zval_get_string(h_ce); ++ ++ if (php_http_match(ce->val, "gzip", PHP_HTTP_MATCH_WORD) ++ || php_http_match(ce->val, "x-gzip", PHP_HTTP_MATCH_WORD) ++ || php_http_match(ce->val, "deflate", PHP_HTTP_MATCH_WORD) + ) { + if (parser->inflate) { + php_http_encoding_stream_reset(&parser->inflate); + } else { - parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0 TSRMLS_CC); ++ parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0); + } - zend_hash_update(&(*message)->hdrs, "X-Original-Content-Encoding", sizeof("X-Original-Content-Encoding"), &h, sizeof(zval *), NULL); - zend_hash_del(&(*message)->hdrs, "Content-Encoding", sizeof("Content-Encoding")); - } else { - zval_ptr_dtor(&h); ++ Z_TRY_ADDREF_P(h_ce); ++ zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), h_ce); ++ zend_hash_str_del(&(*message)->hdrs, "Content-Encoding", lenof("Content-Encoding")); + } ++ zend_string_release(ce); + } + + if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); + } else { - if (h_te) { - if (strstr(Z_STRVAL_PP(h_te), "chunked")) { - parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0 TSRMLS_CC); - php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); - break; - } ++ if (chunked) { ++ parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0); ++ php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); ++ break; + } + - if (h_cr) { ++ if (content_range) { + ulong total = 0, start = 0, end = 0; + - if (!strncasecmp(Z_STRVAL_PP(h_cr), "bytes", lenof("bytes")) - && ( Z_STRVAL_PP(h_cr)[lenof("bytes")] == ':' - || Z_STRVAL_PP(h_cr)[lenof("bytes")] == ' ' - || Z_STRVAL_PP(h_cr)[lenof("bytes")] == '=' ++ if (!strncasecmp(content_range->val, "bytes", lenof("bytes")) ++ && ( content_range->val[lenof("bytes")] == ':' ++ || content_range->val[lenof("bytes")] == ' ' ++ || content_range->val[lenof("bytes")] == '=' + ) + ) { + char *total_at = NULL, *end_at = NULL; - char *start_at = Z_STRVAL_PP(h_cr) + sizeof("bytes"); ++ char *start_at = content_range->val + sizeof("bytes"); + + start = strtoul(start_at, &end_at, 10); + if (end_at) { + end = strtoul(end_at + 1, &total_at, 10); + if (total_at && strncmp(total_at + 1, "*", 1)) { + total = strtoul(total_at + 1, NULL, 10); + } + + if (end >= start && (!total || end <= total)) { + parser->body_length = end + 1 - start; + php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); ++ zend_string_release(content_range); + break; + } + } + } - } + - if (h_cl) { - char *stop; - - if (Z_TYPE_PP(h_cl) == IS_STRING) { - parser->body_length = strtoul(Z_STRVAL_PP(h_cl), &stop, 10); ++ zend_string_release(content_range); ++ } + - if (stop != Z_STRVAL_PP(h_cl)) { - php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); - break; - } - } else if (Z_TYPE_PP(h_cl) == IS_LONG) { - parser->body_length = Z_LVAL_PP(h_cl); - php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); - break; - } ++ if (content_length >= 0) { ++ parser->body_length = content_length; ++ php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); ++ break; + } + + if ((*message)->type == PHP_HTTP_REQUEST) { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); + } else { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); + } + } + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY: + { + if (len) { - /* FIXME: what if we re-use the parser? */ + if (parser->inflate) { + char *dec_str = NULL; + size_t dec_len; + + if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) { + return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); + } + + if (str != buffer->data) { + PTR_FREE(str); + } + str = dec_str; + len = dec_len; + } + + php_stream_write(php_http_message_body_stream((*message)->body), str, len); - + } + + if (cut) { + php_http_buffer_cut(buffer, 0, cut); + } + + if (str != buffer->data) { + PTR_FREE(str); + } + + str = NULL; + len = 0; + cut = 0; + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: + { + str = buffer->data; + len = buffer->used; + cut = len; + + php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: + { + len = MIN(parser->body_length, buffer->used); + str = buffer->data; + cut = len; + + parser->body_length -= len; + + php_http_message_parser_state_push(parser, 2, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED: + { + /* + * - pass available data through the dechunk stream + * - pass decoded data along + * - if stream zeroed: + * Y: - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched + * - body done + * N: - parse ahaed + */ + char *dec_str = NULL; + size_t dec_len; + + if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) { + return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; + } + + str = dec_str; + len = dec_len; + + if (php_http_encoding_stream_done(parser->dechunk)) { + cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used; + php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + } else { + cut = buffer->used; + php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + } + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE: + { + php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); + + if (parser->dechunk && parser->dechunk->ctx) { + char *dec_str = NULL; + size_t dec_len; + + if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) { + return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); + } + php_http_encoding_stream_dtor(parser->dechunk); + + if (dec_str && dec_len) { + str = dec_str; + len = dec_len; + cut = 0; + php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); + } + } + + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL: + { - zval *zcl; - MAKE_STD_ZVAL(zcl); - ZVAL_LONG(zcl, php_http_message_body_size((*message)->body)); - zend_hash_update(&(*message)->hdrs, "Content-Length", sizeof("Content-Length"), &zcl, sizeof(zval *), NULL); ++ zval zcl; ++ ++ ZVAL_LONG(&zcl, php_http_message_body_size((*message)->body)); ++ zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &zcl); + break; + } + + case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: + { + char *ptr = buffer->data; + + while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { + ++ptr; + } + + php_http_buffer_cut(buffer, 0, ptr - buffer->data); + + if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) { + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; + } + break; + } + } + } + + return php_http_message_parser_state_is(parser); + } + + zend_class_entry *php_http_message_parser_class_entry; + static zend_object_handlers php_http_message_parser_object_handlers; + -zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC) ++zend_object *php_http_message_parser_object_new(zend_class_entry *ce) + { - return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_message_parser_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC) ++php_http_message_parser_object_t *php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser) + { + php_http_message_parser_object_t *o; + - o = ecalloc(1, sizeof(php_http_message_parser_object_t)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - if (ptr) { - *ptr = o; - } ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + + if (parser) { + o->parser = parser; + } else { - o->parser = php_http_message_parser_init(NULL TSRMLS_CC); ++ o->parser = php_http_message_parser_init(NULL); + } - o->buffer = php_http_buffer_new(); ++ php_http_buffer_init(&o->buffer); ++ o->zo.handlers = &php_http_message_parser_object_handlers; + - o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC); - o->zv.handlers = &php_http_message_parser_object_handlers; - - return o->zv; ++ return o; + } + -void php_http_message_parser_object_free(void *object TSRMLS_DC) ++void php_http_message_parser_object_free(zend_object *object) + { - php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object; ++ php_http_message_parser_object_t *o = PHP_HTTP_OBJ(object, NULL); + + if (o->parser) { + php_http_message_parser_free(&o->parser); + } - if (o->buffer) { - php_http_buffer_free(&o->buffer); - } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); ++ php_http_buffer_dtor(&o->buffer); ++ zend_object_std_dtor(object); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessageParser, getState) + { - php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); ++ php_http_message_parser_object_t *parser_obj = PHP_HTTP_OBJ(NULL, getThis()); + + zend_parse_parameters_none(); + /* always return the real state */ + RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessageParser, parse) + { + php_http_message_parser_object_t *parser_obj; + zval *zmsg; + char *data_str; - int data_len; - long flags; ++ size_t data_len; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); + - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - php_http_buffer_append(parser_obj->buffer, data_str, data_len); - RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message)); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); ++ php_http_buffer_append(&parser_obj->buffer, data_str, data_len); ++ RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, &parser_obj->buffer, flags, &parser_obj->parser->message)); + ++ ZVAL_DEREF(zmsg); + zval_dtor(zmsg); ++ ZVAL_NULL(zmsg); + if (parser_obj->parser->message) { - ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); ++ php_http_message_t *msg_cpy = php_http_message_copy(parser_obj->parser->message, NULL); ++ php_http_message_object_t *msg_obj = php_http_message_object_new_ex(php_http_message_class_entry, msg_cpy); ++ ZVAL_OBJ(zmsg, &msg_obj->zo); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) + ZEND_END_ARG_INFO(); + static PHP_METHOD(HttpMessageParser, stream) + { + php_http_message_parser_object_t *parser_obj; + zend_error_handling zeh; + zval *zmsg, *zstream; + php_stream *s; - long flags; ++ zend_long flags; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &zstream, &flags, &zmsg), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); - php_stream_from_zval(s, &zstream); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh); ++ php_stream_from_zval(s, zstream); ++ zend_restore_error_handling(&zeh); + - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message)); ++ parser_obj = PHP_HTTP_OBJ(NULL, getThis()); ++ RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, &parser_obj->buffer, s, flags, &parser_obj->parser->message)); + ++ ZVAL_DEREF(zmsg); + zval_dtor(zmsg); ++ ZVAL_NULL(zmsg); + if (parser_obj->parser->message) { - ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); ++ php_http_message_t *msg_cpy = php_http_message_copy(parser_obj->parser->message, NULL); ++ php_http_message_object_t *msg_obj = php_http_message_object_new_ex(php_http_message_class_entry, msg_cpy); ++ ZVAL_OBJ(zmsg, &msg_obj->zo); + } + } + + static zend_function_entry php_http_message_parser_methods[] = { + PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} + }; + + PHP_MINIT_FUNCTION(http_message_parser) + { + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods); - php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_message_parser_class_entry = zend_register_internal_class(&ce); + memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new; + php_http_message_parser_object_handlers.clone_obj = NULL; - - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC); - - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL TSRMLS_CC); - zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC); ++ php_http_message_parser_object_handlers.free_obj = php_http_message_parser_object_free; ++ php_http_message_parser_object_handlers.offset = XtOffsetOf(php_http_message_parser_object_t, zo); ++ ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY); ++ ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_UPDATE_CL"), PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL); ++ zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_message_parser.h index 0000000,0bac9da..fa63858 mode 000000,100644..100644 --- a/src/php_http_message_parser.h +++ b/src/php_http_message_parser.h @@@ -1,0 -1,85 +1,81 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_MESSAGE_PARSER_H + #define PHP_HTTP_MESSAGE_PARSER_H + + #include "php_http_header_parser.h" + #include "php_http_encoding.h" + #include "php_http_message.h" + + typedef enum php_http_message_parser_state { + PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE = FAILURE, + PHP_HTTP_MESSAGE_PARSER_STATE_START = 0, + PHP_HTTP_MESSAGE_PARSER_STATE_HEADER, + PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE, + PHP_HTTP_MESSAGE_PARSER_STATE_BODY, + PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB, + PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, + PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, + PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, + PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, + PHP_HTTP_MESSAGE_PARSER_STATE_DONE + } php_http_message_parser_state_t; + + #define PHP_HTTP_MESSAGE_PARSER_CLEANUP 0x1 + #define PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES 0x2 + #define PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS 0x4 + #define PHP_HTTP_MESSAGE_PARSER_GREEDY 0x8 + + typedef struct php_http_message_parser { + php_http_header_parser_t header; + zend_ptr_stack stack; + size_t body_length; + php_http_message_t *message; + php_http_encoding_stream_t *dechunk; + php_http_encoding_stream_t *inflate; -#ifdef ZTS - void ***ts; -#endif + } php_http_message_parser_t; + -PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC); ++PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser); + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_push(php_http_message_parser_t *parser, unsigned argc, ...); + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_is(php_http_message_parser_t *parser); + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_pop(php_http_message_parser_t *parser); + PHP_HTTP_API void php_http_message_parser_dtor(php_http_message_parser_t *parser); + PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parser); + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message); + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buffer, php_stream *s, unsigned flags, php_http_message_t **message); + + typedef struct php_http_message_parser_object { - zend_object zo; - zend_object_value zv; - php_http_buffer_t *buffer; ++ php_http_buffer_t buffer; + php_http_message_parser_t *parser; ++ zend_object zo; + } php_http_message_parser_object_t; + + PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry; + + PHP_MINIT_FUNCTION(http_message_parser); + -zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC); -void php_http_message_parser_object_free(void *object TSRMLS_DC); ++zend_object *php_http_message_parser_object_new(zend_class_entry *ce); ++php_http_message_parser_object_t *php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser); ++void php_http_message_parser_object_free(zend_object *object); + + #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 --cc src/php_http_misc.c index 0000000,8e2227d..4adab81 mode 000000,100644..100644 --- a/src/php_http_misc.c +++ b/src/php_http_misc.c @@@ -1,0 -1,283 +1,272 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + #include + + /* SLEEP */ + + void php_http_sleep(double s) + { + #if defined(PHP_WIN32) + Sleep((DWORD) PHP_HTTP_MSEC(s)); + #elif defined(HAVE_USLEEP) + usleep(PHP_HTTP_USEC(s)); + #elif defined(HAVE_NANOSLEEP) + struct timespec req, rem; + + req.tv_sec = (time_t) s; + req.tv_nsec = PHP_HTTP_NSEC(s) % PHP_HTTP_NANOSEC; + + while (nanosleep(&req, &rem) && (errno == EINTR) && (PHP_HTTP_NSEC(rem.tv_sec) + rem.tv_nsec) > PHP_HTTP_NSEC(PHP_HTTP_DIFFSEC))) { + req.tv_sec = rem.tv_sec; + req.tv_nsec = rem.tv_nsec; + } + #else + struct timeval timeout; + + timeout.tv_sec = (time_t) s; + timeout.tv_usec = PHP_HTTP_USEC(s) % PHP_HTTP_MCROSEC; + + select(0, NULL, NULL, NULL, &timeout); + #endif + } + + + /* STRING UTILITIES */ + + int php_http_match(const char *haystack_str, const char *needle_str, int flags) + { + int result = 0; + + if (!haystack_str || !needle_str) { + return result; + } + + if (flags & PHP_HTTP_MATCH_FULL) { + if (flags & PHP_HTTP_MATCH_CASE) { + result = !strcmp(haystack_str, needle_str); + } else { + result = !strcasecmp(haystack_str, needle_str); + } + } else { + const char *found; + char *haystack = estrdup(haystack_str), *needle = estrdup(needle_str); + + if (flags & PHP_HTTP_MATCH_CASE) { + found = zend_memnstr(haystack, needle, strlen(needle), haystack+strlen(haystack)); + } else { + found = php_stristr(haystack, needle, strlen(haystack), strlen(needle)); + } + + if (found) { + if (!(flags & PHP_HTTP_MATCH_WORD) + || ( (found == haystack || !PHP_HTTP_IS_CTYPE(alnum, *(found - 1))) + && (!*(found + strlen(needle)) || !PHP_HTTP_IS_CTYPE(alnum, *(found + strlen(needle)))) + ) + ) { + result = 1; + } + } + + PTR_FREE(haystack); + PTR_FREE(needle); + } + + return result; + } + + char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen) + { + size_t i = 1; + int wasalpha; + + if (key && key_len) { + if ((wasalpha = PHP_HTTP_IS_CTYPE(alpha, key[0]))) { + key[0] = (char) (uctitle ? PHP_HTTP_TO_CTYPE(upper, key[0]) : PHP_HTTP_TO_CTYPE(lower, key[0])); + } + PHP_HTTP_DUFF(key_len, + if (PHP_HTTP_IS_CTYPE(alpha, key[i])) { + key[i] = (char) (((!wasalpha) && uctitle) ? PHP_HTTP_TO_CTYPE(upper, key[i]) : PHP_HTTP_TO_CTYPE(lower, key[i])); + wasalpha = 1; + } else { + if (xhyphen && (key[i] == '_')) { + key[i] = '-'; + } + wasalpha = 0; + } + ++i; + ); + } + return key; + } + + -size_t php_http_boundary(char *buf, size_t buf_len TSRMLS_DC) ++size_t php_http_boundary(char *buf, size_t buf_len) + { - return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C)); ++ return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time() * php_combined_lcg()); + } + + int php_http_select_str(const char *cmp, int argc, ...) + { + va_list argv; + int match = -1; + + if (cmp && argc > 0) { + int i; + + va_start(argv, argc); + for (i = 0; i < argc; ++i) { + const char *test = va_arg(argv, const char *); + + if (!strcasecmp(cmp, test)) { + match = i; + break; + } + } + va_end(argv); + } + + return match; + } + + + /* ARRAYS */ + -unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...) ++unsigned php_http_array_list(HashTable *ht, unsigned argc, ...) + { - HashPosition pos; + unsigned argl = 0; + va_list argv; ++ zval *data; + + va_start(argv, argc); - for ( zend_hash_internal_pointer_reset_ex(ht, &pos); - SUCCESS == zend_hash_has_more_elements_ex(ht, &pos) && (argl < argc); - zend_hash_move_forward_ex(ht, &pos)) - { - zval **data, ***argp = (zval ***) va_arg(argv, zval ***); - - if (SUCCESS == zend_hash_get_current_data_ex(ht, (void *) &data, &pos)) { - *argp = data; - ++argl; - } - } ++ ZEND_HASH_FOREACH_VAL(ht, data) { ++ zval **argp = (zval **) va_arg(argv, zval **); ++ *argp = data; ++ ++argl; ++ } ZEND_HASH_FOREACH_END(); + va_end(argv); + + return argl; + } + -void php_http_array_copy_strings(void *zpp) ++void php_http_array_copy_strings(zval *zp) + { - zval **zvpp = ((zval **) zpp); - - *zvpp = php_http_zsep(1, IS_STRING, *zvpp); ++ Z_TRY_ADDREF_P(zp); ++ convert_to_string_ex(zp); + } + -int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) ++int php_http_array_apply_append_func(zval *value, int num_args, va_list args, zend_hash_key *hash_key) + { + int flags; + char *key = NULL; + HashTable *dst; - zval **data = NULL, *value = *((zval **) pDest); ++ zval *data = NULL; + + dst = va_arg(args, HashTable *); + flags = va_arg(args, int); + - if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) { - if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) { - key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1); - zend_hash_find(dst, key, hash_key->nKeyLength, (void *) &data); - } else if (hash_key->nKeyLength) { - zend_hash_quick_find(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &data); ++ if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->key) { ++ if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->key) { ++ key = php_http_pretty_key(estrndup(hash_key->key->val, hash_key->key->len), hash_key->key->len, 1, 1); ++ data = zend_hash_str_find(dst, key, hash_key->key->len); ++ } else if (hash_key->key) { ++ data = zend_hash_find(dst, hash_key->key); + } else { - zend_hash_index_find(dst, hash_key->h, (void *) &data); ++ data = zend_hash_index_find(dst, hash_key->h); + } + + if (flags & ARRAY_JOIN_STRINGIFY) { - value = php_http_zsep(1, IS_STRING, value); - } else { - Z_ADDREF_P(value); ++ convert_to_string_ex(value); + } ++ Z_ADDREF_P(value); + + if (data) { - if (Z_TYPE_PP(data) != IS_ARRAY) { - convert_to_array(*data); ++ if (Z_TYPE_P(data) != IS_ARRAY) { ++ convert_to_array(data); + } - add_next_index_zval(*data, value); ++ add_next_index_zval(data, value); + } else if (key) { - zend_symtable_update(dst, key, hash_key->nKeyLength, &value, sizeof(zval *), NULL); - } else if (hash_key->nKeyLength) { - zend_hash_quick_add(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &value, sizeof(zval *), NULL); ++ zend_hash_str_update(dst, key, hash_key->key->len, value); ++ } else if (hash_key->key) { ++ zend_hash_update(dst, hash_key->key, value); + } else { - zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); ++ zend_hash_index_update(dst, hash_key->h, value); + } + + if (key) { + efree(key); + } + } + + return ZEND_HASH_APPLY_KEEP; + } + -int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) ++int php_http_array_apply_merge_func(zval *value, int num_args, va_list args, zend_hash_key *hash_key) + { + int flags; + char *key = NULL; + HashTable *dst; - zval *value = *((zval **) pDest); + + dst = va_arg(args, HashTable *); + flags = va_arg(args, int); + - if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->nKeyLength) { ++ if ((!(flags & ARRAY_JOIN_STRONLY)) || hash_key->key) { + if (flags & ARRAY_JOIN_STRINGIFY) { - value = php_http_zsep(1, IS_STRING, value); - } else { - Z_ADDREF_P(value); ++ convert_to_string_ex(value); + } + - if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->nKeyLength) { - key = php_http_pretty_key(estrndup(hash_key->arKey, hash_key->nKeyLength - 1), hash_key->nKeyLength - 1, 1, 1); - zend_hash_update(dst, key, hash_key->nKeyLength, (void *) &value, sizeof(zval *), NULL); ++ Z_TRY_ADDREF_P(value); ++ ++ if ((flags & ARRAY_JOIN_PRETTIFY) && hash_key->key) { ++ key = php_http_pretty_key(estrndup(hash_key->key->val, hash_key->key->len), hash_key->key->len, 1, 1); ++ zend_hash_str_update(dst, key, hash_key->key->len, value); + efree(key); - } else if (hash_key->nKeyLength) { - zend_hash_quick_update(dst, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &value, sizeof(zval *), NULL); ++ } else if (hash_key->key) { ++ zend_hash_update(dst, hash_key->key, value); + } else { - zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); ++ zend_hash_index_update(dst, hash_key->h, value); + } + } + + return ZEND_HASH_APPLY_KEEP; + } + + /* PASS CALLBACK */ + + size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len) + { + php_http_pass_fcall_arg_t *fcd = cb_arg; - zval *zdata; - TSRMLS_FETCH_FROM_CTX(fcd->ts); ++ zval zdata; + - MAKE_STD_ZVAL(zdata); - ZVAL_STRINGL(zdata, str, len, 1); - if (SUCCESS == zend_fcall_info_argn(&fcd->fci TSRMLS_CC, 2, &fcd->fcz, &zdata)) { - zend_fcall_info_call(&fcd->fci, &fcd->fcc, NULL, NULL TSRMLS_CC); ++ ZVAL_STRINGL(&zdata, str, len); ++ if (SUCCESS == zend_fcall_info_argn(&fcd->fci, 2, &fcd->fcz, &zdata)) { ++ zend_fcall_info_call(&fcd->fci, &fcd->fcc, NULL, NULL); + zend_fcall_info_args_clear(&fcd->fci, 0); + } + zval_ptr_dtor(&zdata); + return len; + } + + + /* ZEND */ + + /* + * 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 --cc src/php_http_misc.h index 0000000,a4f579d..e8d900b mode 000000,100644..100644 --- a/src/php_http_misc.h +++ b/src/php_http_misc.h @@@ -1,0 -1,366 +1,282 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_MISC_H + #define PHP_HTTP_MISC_H + + /* DEFAULTS */ + + /* DATE FORMAT RFC1123 */ + #define PHP_HTTP_DATE_FORMAT "D, d M Y H:i:s \\G\\M\\T" + + /* CR LF */ + #define PHP_HTTP_CRLF "\r\n" + + /* def URL arg separator */ + #define PHP_HTTP_URL_ARGSEP "&" + + /* send buffer size */ + #define PHP_HTTP_SENDBUF_SIZE 40960 + + /* allowed characters of header field names */ + #define PHP_HTTP_HEADER_NAME_CHARS "!#$%&'*+-.^_`|~1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + + /* SLEEP */ + + #define PHP_HTTP_DIFFSEC (0.001) + #define PHP_HTTP_MLLISEC (1000) + #define PHP_HTTP_MCROSEC (1000 * 1000) + #define PHP_HTTP_NANOSEC (1000 * 1000 * 1000) + #define PHP_HTTP_MSEC(s) ((long)(s * PHP_HTTP_MLLISEC)) + #define PHP_HTTP_USEC(s) ((long)(s * PHP_HTTP_MCROSEC)) + #define PHP_HTTP_NSEC(s) ((long)(s * PHP_HTTP_NANOSEC)) + + PHP_HTTP_API void php_http_sleep(double s); + + /* STRING UTILITIES */ + + #ifndef PTR_SET + # define PTR_SET(STR, SET) \ + { \ + PTR_FREE(STR); \ + STR = SET; \ + } + #endif + + #define STR_PTR(s) (s?s:"") + + #define lenof(S) (sizeof(S) - 1) + + #define PHP_HTTP_MATCH_LOOSE 0 + #define PHP_HTTP_MATCH_CASE 0x01 + #define PHP_HTTP_MATCH_WORD 0x10 + #define PHP_HTTP_MATCH_FULL 0x20 + #define PHP_HTTP_MATCH_STRICT (PHP_HTTP_MATCH_CASE|PHP_HTTP_MATCH_FULL) + + int php_http_match(const char *haystack, const char *needle, int flags); + char *php_http_pretty_key(register char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen); -size_t php_http_boundary(char *buf, size_t len TSRMLS_DC); ++size_t php_http_boundary(char *buf, size_t len); + int php_http_select_str(const char *cmp, int argc, ...); + + /* See "A Reusable Duff Device" By Ralf Holly, August 01, 2005 */ + #define PHP_HTTP_DUFF_BREAK() times_=1 + #define PHP_HTTP_DUFF(c, a) do { \ + size_t count_ = (c); \ + size_t times_ = (count_ + 7) >> 3; \ + switch (count_ & 7){ \ + case 0: do { \ + a; \ + case 7: \ + a; \ + case 6: \ + a; \ + case 5: \ + a; \ + case 4: \ + a; \ + case 3: \ + a; \ + case 2: \ + a; \ + case 1: \ + a; \ + } while (--times_ > 0); \ + } \ + } while (0) + + static inline const char *php_http_locate_str(register const char *h, size_t h_len, const char *n, size_t n_len) + { + if (!n_len || !h_len || h_len < n_len) { + return NULL; + } + + PHP_HTTP_DUFF(h_len - n_len + 1, + if (*h == *n && !strncmp(h + 1, n + 1, n_len - 1)) { + return h; + } + ++h; + ); + + return NULL; + } + + static inline const char *php_http_locate_eol(const char *line, int *eol_len) + { + const char *eol = strpbrk(line, "\r\n"); + + if (eol_len) { + *eol_len = eol ? ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1) : 0; + } + return eol; + } + + static inline const char *php_http_locate_bin_eol(const char *bin, size_t len, int *eol_len) + { + register const char *eol = bin; + + if (len > 0) { + PHP_HTTP_DUFF(len, + if (*eol == '\r' || *eol == '\n') { + if (eol_len) { + *eol_len = ((eol[0] == '\r' && eol[1] == '\n') ? 2 : 1); + } + return eol; + } + ++eol; + ); + } + return NULL; + } + + /* ZEND */ + -#if PHP_VERSION_ID < 50400 -# define object_properties_init(o, ce) zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*)) -# define PHP_HTTP_ZEND_LITERAL_DC -# define PHP_HTTP_ZEND_LITERAL_CC -# define PHP_HTTP_ZEND_LITERAL_CCN -# define ZVAL_COPY_VALUE(zv, arr) do { \ - (zv)->value = (arr)->value; \ - Z_TYPE_P(zv) = Z_TYPE_P(arr); \ - } while (0) -#else -# define PHP_HTTP_ZEND_LITERAL_DC , const zend_literal *literal_key -# define PHP_HTTP_ZEND_LITERAL_CC , (literal_key) -# define PHP_HTTP_ZEND_LITERAL_CCN , NULL ++#ifdef PHP_DEBUG ++# undef HASH_OF ++# define HASH_OF(p) ((HashTable*)(Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties((p)) : NULL)))) + #endif + -#if PHP_VERSION_ID < 50500 -#undef SUCCESS -#undef FAILURE -typedef enum { - SUCCESS = 0, - FAILURE = -1 -} ZEND_RESULT_CODE; -#endif - -#if PHP_VERSION_ID < 50700 -# define z_is_true zend_is_true -#else -# define z_is_true(z) zend_is_true(z TSRMLS_CC) -#endif - -#define INIT_PZVAL_ARRAY(zv, ht) \ - { \ - INIT_PZVAL((zv)); \ - Z_TYPE_P(zv) = IS_ARRAY; \ - Z_ARRVAL_P(zv) = (ht); \ - } - -static inline zval *php_http_zconv(int type, zval *z) ++static inline void *PHP_HTTP_OBJ(zend_object *zo, zval *zv) + { - switch (type) { - case IS_NULL: convert_to_null_ex(&z); break; - case IS_BOOL: convert_to_boolean_ex(&z); break; - case IS_LONG: convert_to_long_ex(&z); break; - case IS_DOUBLE: convert_to_double_ex(&z); break; - case IS_STRING: convert_to_string_ex(&z); break; - case IS_ARRAY: convert_to_array_ex(&z); break; - case IS_OBJECT: convert_to_object_ex(&z); break; ++ if (!zo) { ++ zo = Z_OBJ_P(zv); + } - return z; ++ return (char *) zo - zo->handlers->offset; + } + -static inline zval *php_http_ztyp(int type, zval *z) ++static inline zend_string *php_http_cs2zs(char *s, size_t l) + { - SEPARATE_ARG_IF_REF(z); - return (Z_TYPE_P(z) == type) ? z : php_http_zconv(type, z); -} ++ zend_string *str = erealloc(s, sizeof(*str) + l); + -static inline zval *php_http_zsep(zend_bool add_ref, int type, zval *z) -{ - if (add_ref) { - Z_ADDREF_P(z); - } - if (Z_TYPE_P(z) != type) { - return php_http_zconv(type, z); - } else { - SEPARATE_ZVAL_IF_NOT_REF(&z); - return z; - } ++ memmove(str->val, str, l); ++ str->val[l] = 0; ++ str->len = l; ++ str->h = 0; ++ ++ GC_REFCOUNT(str) = 1; ++ GC_TYPE_INFO(str) = IS_STRING; ++ ++ return str; + } + -static inline ZEND_RESULT_CODE php_http_ini_entry(const char *name_str, size_t name_len, const char **value_str, size_t *value_len, zend_bool orig TSRMLS_DC) ++static inline ZEND_RESULT_CODE php_http_ini_entry(const char *name_str, size_t name_len, const char **val_str, size_t *val_len, zend_bool orig) + { + zend_ini_entry *ini_entry; + - if (SUCCESS == zend_hash_find(EG(ini_directives), name_str, name_len + 1, (void *) &ini_entry)) { ++ if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name_str, name_len))) { + if (orig && ini_entry->modified) { - *value_str = ini_entry->orig_value; - *value_len = (size_t) ini_entry->orig_value_length; ++ *val_str = ini_entry->orig_value->val; ++ *val_len = ini_entry->orig_value->len; + } else { - *value_str = ini_entry->value; - *value_len = (size_t) ini_entry->value_length; ++ *val_str = ini_entry->value->val; ++ *val_len = ini_entry->value->len; + } + return SUCCESS; + } + return FAILURE; + } + ++#define Z_ISUSER(zv) (Z_TYPE(zv) <= 10) ++#define Z_ISUSER_P(zvp) Z_ISUSER(*(zvp)) ++ + /* return object(values) */ ++#define ZVAL_OBJECT(z, o, addref) \ ++ ZVAL_OBJ(z, o); \ ++ if (addref) { \ ++ Z_ADDREF_P(z); \ ++ } + #define RETVAL_OBJECT(o, addref) \ - RETVAL_OBJVAL((o)->value.obj, addref) ++ ZVAL_OBJECT(return_value, o, addref) + #define RETURN_OBJECT(o, addref) \ + RETVAL_OBJECT(o, addref); \ + return -#define RETVAL_OBJVAL(ov, addref) \ - ZVAL_OBJVAL(return_value, ov, addref) -#define RETURN_OBJVAL(ov, addref) \ - RETVAL_OBJVAL(ov, addref); \ - return -#define ZVAL_OBJVAL(zv, ov, addref) \ - (zv)->type = IS_OBJECT; \ - (zv)->value.obj = (ov);\ - if (addref && Z_OBJ_HT_P(zv)->add_ref) { \ - Z_OBJ_HT_P(zv)->add_ref((zv) TSRMLS_CC); \ - } - -#define Z_OBJ_DELREF(z) \ - if (Z_OBJ_HT(z)->del_ref) { \ - Z_OBJ_HT(z)->del_ref(&(z) TSRMLS_CC); \ - } -#define Z_OBJ_ADDREF(z) \ - if (Z_OBJ_HT(z)->add_ref) { \ - Z_OBJ_HT(z)->add_ref(&(z) TSRMLS_CC); \ - } -#define Z_OBJ_DELREF_P(z) \ - if (Z_OBJ_HT_P(z)->del_ref) { \ - Z_OBJ_HT_P(z)->del_ref((z) TSRMLS_CC); \ - } -#define Z_OBJ_ADDREF_P(z) \ - if (Z_OBJ_HT_P(z)->add_ref) { \ - Z_OBJ_HT_P(z)->add_ref((z) TSRMLS_CC); \ - } -#define Z_OBJ_DELREF_PP(z) \ - if (Z_OBJ_HT_PP(z)->del_ref) { \ - Z_OBJ_HT_PP(z)->del_ref(*(z) TSRMLS_CC); \ - } -#define Z_OBJ_ADDREF_PP(z) \ - if (Z_OBJ_HT_PP(z)->add_ref) { \ - Z_OBJ_HT_PP(z)->add_ref(*(z) TSRMLS_CC); \ - } + + #define EMPTY_FUNCTION_ENTRY {NULL, NULL, NULL, 0, 0} + + #define PHP_MINIT_CALL(func) PHP_MINIT(func)(INIT_FUNC_ARGS_PASSTHRU) + #define PHP_RINIT_CALL(func) PHP_RINIT(func)(INIT_FUNC_ARGS_PASSTHRU) + #define PHP_MSHUTDOWN_CALL(func) PHP_MSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU) + #define PHP_RSHUTDOWN_CALL(func) PHP_RSHUTDOWN(func)(SHUTDOWN_FUNC_ARGS_PASSTHRU) + + /* ARRAYS */ + -#ifndef HASH_KEY_NON_EXISTENT -# define HASH_KEY_NON_EXISTENT HASH_KEY_NON_EXISTANT -#endif - -PHP_HTTP_API unsigned php_http_array_list(HashTable *ht TSRMLS_DC, unsigned argc, ...); ++PHP_HTTP_API unsigned php_http_array_list(HashTable *ht, unsigned argc, ...); + -typedef struct php_http_array_hashkey { - char *str; - uint len; - ulong num; - uint dup:1; - uint type:31; -} php_http_array_hashkey_t; -#define php_http_array_hashkey_init(dup) {NULL, 0, 0, (dup), 0} ++typedef struct php_http_arrkey { ++ zend_ulong h; ++ zend_string *key; ++ unsigned allocated:1; ++ unsigned stringified:1; ++} php_http_arrkey_t; + -static inline void php_http_array_hashkey_stringify(php_http_array_hashkey_t *key) ++static inline void *php_http_arrkey_stringify(php_http_arrkey_t *arrkey, zend_hash_key *key) + { - if (key->type != HASH_KEY_IS_STRING) { - key->len = spprintf(&key->str, 0, "%lu", key->num) + 1; ++ if (arrkey) { ++ arrkey->allocated = 0; ++ } else { ++ arrkey = emalloc(sizeof(*arrkey)); ++ arrkey->allocated = 1; ++ } ++ ++ if (key) { ++ memcpy(arrkey, key, sizeof(*key)); ++ } ++ if ((arrkey->stringified = !arrkey->key)) { ++ arrkey->key = zend_long_to_str(arrkey->h); + } ++ return arrkey; + } + -static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key) ++static inline void php_http_arrkey_dtor(php_http_arrkey_t *arrkey) + { - if (key->type != HASH_KEY_IS_STRING || key->dup) { - PTR_FREE(key->str); ++ if (arrkey->stringified) { ++ zend_string_release(arrkey->key); ++ } ++ if (arrkey->allocated) { ++ efree(arrkey); + } + } + -#define FOREACH_VAL(pos, array, val) FOREACH_HASH_VAL(pos, HASH_OF(array), val) -#define FOREACH_HASH_VAL(pos, hash, val) \ - for ( zend_hash_internal_pointer_reset_ex(hash, &pos); \ - zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \ - zend_hash_move_forward_ex(hash, &pos)) - -#define FOREACH_KEY(pos, array, key) FOREACH_HASH_KEY(pos, HASH_OF(array), key) -#define FOREACH_HASH_KEY(pos, hash, _key) \ - for ( zend_hash_internal_pointer_reset_ex(hash, &pos); \ - ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT; \ - zend_hash_move_forward_ex(hash, &pos)) \ - -#define FOREACH_KEYVAL(pos, array, key, val) FOREACH_HASH_KEYVAL(pos, HASH_OF(array), key, val) -#define FOREACH_HASH_KEYVAL(pos, hash, _key, val) \ - for ( zend_hash_internal_pointer_reset_ex(hash, &pos); \ - ((_key).type = zend_hash_get_current_key_ex(hash, &(_key).str, &(_key).len, &(_key).num, (zend_bool) (_key).dup, &pos)) != HASH_KEY_NON_EXISTANT && \ - zend_hash_get_current_data_ex(hash, (void *) &val, &pos) == SUCCESS; \ - zend_hash_move_forward_ex(hash, &pos)) - -#define array_copy(src, dst) zend_hash_copy(dst, src, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)) -#define array_copy_strings(src, dst) zend_hash_copy(dst, src, php_http_array_copy_strings, NULL, sizeof(zval *)) ++#define array_copy(src, dst) zend_hash_copy(dst, src, (copy_ctor_func_t) zval_add_ref) ++#define array_copy_strings(src, dst) zend_hash_copy(dst, src, php_http_array_copy_strings) + #define ARRAY_JOIN_STRONLY 0x01 + #define ARRAY_JOIN_PRETTIFY 0x02 + #define ARRAY_JOIN_STRINGIFY 0x04 -#define array_join(src, dst, append, flags) zend_hash_apply_with_arguments(src TSRMLS_CC, (append)?php_http_array_apply_append_func:php_http_array_apply_merge_func, 2, dst, (int)flags) ++#define array_join(src, dst, append, flags) zend_hash_apply_with_arguments(src, (append)?php_http_array_apply_append_func:php_http_array_apply_merge_func, 2, dst, (int)flags) + -void php_http_array_copy_strings(void *zpp); -int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key); -int php_http_array_apply_merge_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key); ++void php_http_array_copy_strings(zval *zp); ++int php_http_array_apply_append_func(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key); ++int php_http_array_apply_merge_func(zval *pDest, int num_args, va_list args, zend_hash_key *hash_key); + + /* PASS CALLBACK */ + + typedef size_t (*php_http_pass_callback_t)(void *cb_arg, const char *str, size_t len); + typedef size_t (*php_http_pass_php_http_buffer_callback_t)(void *cb_arg, php_http_buffer_t *str); + typedef size_t (*php_http_pass_format_callback_t)(void *cb_arg, const char *fmt, ...); + + typedef struct php_http_pass_fcall_arg { - zval *fcz; ++ zval fcz; + zend_fcall_info fci; + zend_fcall_info_cache fcc; -#ifdef ZTS - void ***ts; -#endif + } php_http_pass_fcall_arg_t; + + PHP_HTTP_API size_t php_http_pass_fcall_callback(void *cb_arg, const char *str, size_t len); + + #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 --cc src/php_http_negotiate.c index 0000000,a74875b..d2410f8 mode 000000,100644..100644 --- a/src/php_http_negotiate.c +++ b/src/php_http_negotiate.c @@@ -1,0 -1,173 +1,174 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -static int php_http_negotiate_sort(const void *a, const void *b TSRMLS_DC) ++static int php_http_negotiate_sort(const void *first, const void *second) + { - zval result, *first, *second; ++ zval result; ++ Bucket *b1 = (Bucket *) first, *b2 = (Bucket *) second; + - first = *((zval **) (*((Bucket **) a))->pData); - second= *((zval **) (*((Bucket **) b))->pData); - - if (numeric_compare_function(&result, first, second TSRMLS_CC) != SUCCESS) { ++ if (numeric_compare_function(&result, &b1->val, &b2->val)!= SUCCESS) { + return 0; + } + return (Z_LVAL(result) > 0 ? -1 : (Z_LVAL(result) < 0 ? 1 : 0)); + } + + #define M_PRI 5 + #define M_SEC 2 + #define M_ANY 1 + #define M_NOT 0 -#define M_ALL -1 ++#define M_ALL ~0 + static inline unsigned php_http_negotiate_match(const char *param_str, size_t param_len, const char *supported_str, size_t supported_len, const char *sep_str, size_t sep_len) + { - int match = M_NOT; ++ unsigned match = M_NOT; + + if (param_len == supported_len && !strncasecmp(param_str, supported_str, param_len)) { + /* that was easy */ + match = M_ALL; + } else if (sep_str && sep_len) { + const char *param_sec = php_http_locate_str(param_str, param_len, sep_str, sep_len); + size_t param_pri_len = param_sec ? param_sec - param_str : param_len; + const char *supported_sec = php_http_locate_str(supported_str, supported_len, sep_str, sep_len); + size_t supported_pri_len = supported_sec ? supported_sec - supported_str : supported_len; + size_t cmp_len = MIN(param_pri_len, supported_pri_len); + + if (((*param_str == '*') || (*supported_str == '*')) + || ((param_pri_len == supported_pri_len) && !strncasecmp(param_str, supported_str, param_pri_len)) + || ((!param_sec || !supported_sec) && cmp_len && !strncasecmp(param_str, supported_str, cmp_len)) + ) { + match += M_PRI; + } + + if (param_sec && supported_sec && !strcasecmp(param_sec, supported_sec)) { + match += M_SEC; + } + + if ((param_sec && *(param_sec + sep_len) == '*') + || (supported_sec && *(supported_sec + sep_len) == '*') + || ((*param_str == '*') || (*supported_str == '*')) + ) { + match += M_ANY; + } + } + #if 0 + fprintf(stderr, "match: %s == %s => %u\n", supported_str, param_str, match); + #endif + return match; + } - -static int php_http_negotiate_reduce(void *p TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) ++static int php_http_negotiate_reduce(zval *p, int num_args, va_list args, zend_hash_key *hash_key) + { + unsigned best_match = 0; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **q = NULL, **val, *supported = php_http_ztyp(IS_STRING, *(zval **)p); ++ php_http_arrkey_t key; ++ zval *value, *q = NULL; ++ zend_string *supported = zval_get_string(p); + HashTable *params = va_arg(args, HashTable *); + HashTable *result = va_arg(args, HashTable *); + const char *sep_str = va_arg(args, const char *); + size_t sep_len = va_arg(args, size_t); + - FOREACH_HASH_KEYVAL(pos, params, key, val) { - if (key.type == HASH_KEY_IS_STRING) { - unsigned match = php_http_negotiate_match(key.str, key.len-1, Z_STRVAL_P(supported), Z_STRLEN_P(supported), sep_str, sep_len); ++ ZEND_HASH_FOREACH_KEY_VAL(params, key.h, key.key, value) ++ { ++ unsigned match; + - if (match > best_match) { - best_match = match; - q = val; - } ++ php_http_arrkey_stringify(&key, NULL); ++ match = php_http_negotiate_match(key.key->val, key.key->len, supported->val, supported->len, sep_str, sep_len); ++ ++ if (match > best_match) { ++ best_match = match; ++ q = value; + } ++ php_http_arrkey_dtor(&key); + } ++ ZEND_HASH_FOREACH_END(); + - if (q && Z_DVAL_PP(q) > 0) { - Z_ADDREF_PP(q); - zend_hash_update(result, Z_STRVAL_P(supported), Z_STRLEN_P(supported) + 1, (void *) q, sizeof(zval *), NULL); ++ if (q && Z_DVAL_P(q) > 0) { ++ Z_TRY_ADDREF_P(q); ++ zend_hash_update(result, supported, q); + } + - zval_ptr_dtor(&supported); ++ zend_string_release(supported); + return ZEND_HASH_APPLY_KEEP; + } + -HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC) ++HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len) + { + HashTable *result = NULL; + + if (value_str && value_len) { + unsigned i = 0; - zval arr, **val, **arg, **zq; - HashPosition pos; ++ zval arr, *val, *arg, *zq; + HashTable params; - php_http_array_hashkey_t key = php_http_array_hashkey_init(1); ++ php_http_arrkey_t key; + php_http_params_opts_t opts; + + zend_hash_init(¶ms, 10, NULL, ZVAL_PTR_DTOR, 0); + php_http_params_opts_default_get(&opts); + opts.input.str = estrndup(value_str, value_len); + opts.input.len = value_len; - php_http_params_parse(¶ms, &opts TSRMLS_CC); ++ php_http_params_parse(¶ms, &opts); + efree(opts.input.str); + - INIT_PZVAL(&arr); + array_init(&arr); + - FOREACH_HASH_KEYVAL(pos, ¶ms, key, val) { ++ ZEND_HASH_FOREACH_KEY_VAL(¶ms, key.h, key.key, val) ++ { + double q; + - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("arguments"), (void *) &arg) - && IS_ARRAY == Z_TYPE_PP(arg) - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(arg), ZEND_STRS("q"), (void *) &zq)) { - zval *tmp = php_http_ztyp(IS_DOUBLE, *zq); - - q = Z_DVAL_P(tmp); - zval_ptr_dtor(&tmp); ++ if ((arg = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("arguments"))) ++ && (IS_ARRAY == Z_TYPE_P(arg)) ++ && (zq = zend_hash_str_find(Z_ARRVAL_P(arg), ZEND_STRL("q")))) { ++ q = zval_get_double(zq); + } else { + q = 1.0 - ++i / 100.0; + } + - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_double_ex(&arr, key.str, key.len, q); ++#if 0 ++ fprintf(stderr, "Q: %s=%1.3f\n", key.key->val, q); ++#endif ++ ++ if (key.key) { ++ add_assoc_double_ex(&arr, key.key->val, key.key->len, q); + } else { - add_index_double(&arr, key.num, q); ++ add_index_double(&arr, key.h, q); + } + - PTR_FREE(key.str); + } ++ ZEND_HASH_FOREACH_END(); + + #if 0 - zend_print_zval_r(&arr, 1 TSRMLS_CC); ++ zend_print_zval_r(&arr, 1); + #endif + + ALLOC_HASHTABLE(result); + zend_hash_init(result, zend_hash_num_elements(supported), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_apply_with_arguments(supported TSRMLS_CC, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len); ++ zend_hash_apply_with_arguments(supported, php_http_negotiate_reduce, 4, Z_ARRVAL(arr), result, primary_sep_str, primary_sep_len); + zend_hash_destroy(¶ms); + zval_dtor(&arr); - zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC); ++ zend_hash_sort(result, php_http_negotiate_sort, 0); + } + + return result; + } + + + + /* + * 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 --cc src/php_http_negotiate.h index 0000000,f7405b5..44f1735 mode 000000,100644..100644 --- a/src/php_http_negotiate.h +++ b/src/php_http_negotiate.h @@@ -1,0 -1,140 +1,139 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_NEGOTIATE_H + #define PHP_HTTP_NEGOTIATE_H + -PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len TSRMLS_DC); ++PHP_HTTP_API HashTable *php_http_negotiate(const char *value_str, size_t value_len, HashTable *supported, const char *primary_sep_str, size_t primary_sep_len); + -static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC) ++static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request) + { + HashTable *result = NULL; + size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC); ++ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request); + + if (value) { - result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC); ++ result = php_http_negotiate(value, length, supported, "-", 1); + } + PTR_FREE(value); + + return result; + } + -static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC) ++static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request) + { + HashTable *result = NULL; + size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC); ++ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request); + + if (value) { - result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); ++ result = php_http_negotiate(value, length, supported, NULL, 0); + } + PTR_FREE(value); + + return result; + } + -static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC) ++static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request) + { + HashTable *result = NULL; + size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC); ++ char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request); + + if (value) { - result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); ++ result = php_http_negotiate(value, length, supported, NULL, 0); + } + PTR_FREE(value); + + return result; + } + -static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC) ++static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request) + { + HashTable *result = NULL; + size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC); ++ char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request); + + if (value) { - result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC); ++ result = php_http_negotiate(value, length, supported, "/", 1); + } + PTR_FREE(value); + + return result; + } + + #define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \ + { \ - zval **value; \ ++ zval *value; \ + \ + zend_hash_internal_pointer_reset((supported)); \ - if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \ - RETVAL_ZVAL(*value, 1, 0); \ ++ if ((value = zend_hash_get_current_data((supported)))) { \ ++ RETVAL_ZVAL(value, 1, 0); \ + } else { \ + RETVAL_NULL(); \ + } \ + } + + #define PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array) \ + PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \ + if (rs_array) { \ - HashPosition pos; \ - zval **value_ptr; \ ++ zval *value; \ + \ - FOREACH_HASH_VAL(pos, supported, value_ptr) { \ - zval *value = php_http_ztyp(IS_STRING, *value_ptr); \ - add_assoc_double(rs_array, Z_STRVAL_P(value), 1.0); \ - zval_ptr_dtor(&value); \ ++ ZEND_HASH_FOREACH_VAL(supported, value) \ ++ { \ ++ zend_string *zs = zval_get_string(value); \ ++ add_assoc_double_ex(rs_array, zs->val, zs->len, 1.0); \ ++ zend_string_release(zs); \ + } \ ++ ZEND_HASH_FOREACH_END(); \ + } + + #define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \ + { \ - char *key; \ - uint key_len; \ - ulong idx; \ ++ zend_string *key; \ ++ zend_ulong idx; \ + \ - if (zend_hash_num_elements(result) && HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key, &key_len, &idx, 1, NULL)) { \ - RETVAL_STRINGL(key, key_len-1, 0); \ ++ if (zend_hash_num_elements(result) && HASH_KEY_IS_STRING == zend_hash_get_current_key(result, &key, &idx)) { \ ++ RETVAL_STR_COPY(key); \ + } else { \ + PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported); \ + } \ + \ + if (rs_array) { \ - zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); \ ++ zend_hash_copy(Z_ARRVAL_P(rs_array), result, (copy_ctor_func_t) zval_add_ref); \ + } \ + \ + zend_hash_destroy(result); \ + FREE_HASHTABLE(result); \ + } + + #define PHP_HTTP_DO_NEGOTIATE(type, supported, rs_array) \ + { \ + HashTable *result; \ - if ((result = php_http_negotiate_ ##type(supported, NULL TSRMLS_CC))) { \ ++ if ((result = php_http_negotiate_ ##type(supported, NULL))) { \ + PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array); \ + } else { \ + PHP_HTTP_DO_NEGOTIATE_HANDLE_DEFAULT(supported, rs_array); \ + } \ + } + - + #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 --cc src/php_http_object.c index 0000000,d57388d..e42998e mode 000000,100644..100644 --- a/src/php_http_object.c +++ b/src/php_http_object.c @@@ -1,0 -1,141 +1,136 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC) ++static zend_object_handlers php_http_object_handlers; ++ ++zend_object *php_http_object_new(zend_class_entry *ce) + { - return php_http_object_new_ex(ce, NULL, NULL TSRMLS_CC); ++ return &php_http_object_new_ex(ce, NULL)->zo; + } + -zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC) ++php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *intern) + { + php_http_object_t *o; + - o = ecalloc(1, sizeof(php_http_object_t)); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); ++ o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce)); ++ zend_object_std_init(&o->zo, ce); ++ object_properties_init(&o->zo, ce); + - if (ptr) { - *ptr = o; - } ++ o->intern = intern; ++ o->zo.handlers = &php_http_object_handlers; + - o->zv.handle = zend_objects_store_put(o, NULL, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC); - o->zv.handlers = zend_get_std_object_handlers(); ++ return o; ++} + - return o->zv; ++void php_http_object_free(zend_object *object) ++{ ++ zend_object_std_dtor(object); + } + -ZEND_RESULT_CODE php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC) ++ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr) + { - zend_object_value ov; ++ void *obj; + + if (!ce) { + ce = parent_ce; - } else if (parent_ce && !instanceof_function(ce, parent_ce TSRMLS_CC)) { - php_http_throw(unexpected_val, "Class %s does not extend %s", ce->name, parent_ce->name); ++ } else if (parent_ce && !instanceof_function(ce, parent_ce)) { ++ php_http_throw(unexpected_val, "Class %s does not extend %s", ce->name->val, parent_ce->name->val); + return FAILURE; + } + - ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC); - if (ovp) { - *ovp = ov; ++ obj = create(ce, intern_ptr); ++ if (obj_ptr) { ++ *obj_ptr = obj; + } + return SUCCESS; + } + -static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC) ++php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len) + { -#if PHP_VERSION_ID >= 50400 - return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth), NULL TSRMLS_CC); -#else - return Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(zmeth), Z_STRLEN_P(zmeth) TSRMLS_CC); -#endif -} - -php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC) -{ - zval *zfn; - + if (!cb) { + cb = ecalloc(1, sizeof(*cb)); + } else { + memset(cb, 0, sizeof(*cb)); + } + - MAKE_STD_ZVAL(zfn); - ZVAL_STRINGL(zfn, method_str, method_len, 1); - + cb->fci.size = sizeof(cb->fci); - cb->fci.function_name = zfn; ++ ZVAL_STRINGL(&cb->fci.function_name, method_str, method_len); + cb->fcc.initialized = 1; + cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject); - cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC); ++ cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL); + + return cb; + } + + void php_http_object_method_dtor(php_http_object_method_t *cb) + { - if (cb->fci.function_name) { - zval_ptr_dtor(&cb->fci.function_name); - cb->fci.function_name = NULL; - } ++ zval_ptr_dtor(&cb->fci.function_name); + } + + void php_http_object_method_free(php_http_object_method_t **cb) + { + if (*cb) { + php_http_object_method_dtor(*cb); + efree(*cb); + *cb = NULL; + } + } + -ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC) ++ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval_ptr, int argc, zval *args) + { + ZEND_RESULT_CODE rv; - zval *retval = NULL; ++ zval retval; + ++ ZVAL_UNDEF(&retval); + Z_ADDREF_P(zobject); - cb->fci.object_ptr = zobject; - cb->fcc.object_ptr = zobject; ++ cb->fci.object = Z_OBJ_P(zobject); ++ cb->fcc.object = Z_OBJ_P(zobject); + - cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval; ++ cb->fci.retval = retval_ptr ? retval_ptr : &retval; + + cb->fci.param_count = argc; + cb->fci.params = args; + + if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) { + cb->fcc.called_scope = Z_OBJCE_P(zobject); - cb->fcc.function_handler = get_object_method(zobject, cb->fci.function_name TSRMLS_CC); ++ cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&Z_OBJ_P(zobject), Z_STR(cb->fci.function_name), NULL); + } + - rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC); ++ rv = zend_call_function(&cb->fci, &cb->fcc); + - zval_ptr_dtor(&zobject); - if (!retval_ptr && retval) { ++ zval_ptr_dtor(zobject); ++ if (!retval_ptr && !Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return rv; + } + ++PHP_MINIT_FUNCTION(http_object) ++{ ++ memcpy(&php_http_object_handlers, zend_get_std_object_handlers(), sizeof(php_http_object_handlers)); ++ php_http_object_handlers.offset = XtOffsetOf(php_http_object_t, zo); ++ ++ return SUCCESS; ++} ++ + /* + * 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 --cc src/php_http_object.h index 0000000,3f75932..20de8c8 mode 000000,100644..100644 --- a/src/php_http_object.h +++ b/src/php_http_object.h @@@ -1,0 -1,49 +1,51 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_OBJECT_H + #define PHP_HTTP_OBJECT_H + + typedef struct php_http_object { ++ void *intern; + zend_object zo; - zend_object_value zv; + } php_http_object_t; + -zend_object_value php_http_object_new(zend_class_entry *ce TSRMLS_DC); -zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, php_http_object_t **ptr TSRMLS_DC); ++zend_object *php_http_object_new(zend_class_entry *ce); ++php_http_object_t *php_http_object_new_ex(zend_class_entry *ce, void *nothing); + -typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC); ++typedef void *(*php_http_new_t)(zend_class_entry *ce, void *); + -ZEND_RESULT_CODE php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC); ++ZEND_RESULT_CODE php_http_new(void **obj_ptr, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr); ++ ++PHP_MINIT_FUNCTION(http_object); + + typedef struct php_http_method { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + } php_http_object_method_t; + -php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC); -ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC); ++php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len); ++ZEND_RESULT_CODE php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval *retval, int argc, zval *args); + void php_http_object_method_dtor(php_http_object_method_t *cb); + void php_http_object_method_free(php_http_object_method_t **cb); + + #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 --cc src/php_http_options.c index 0000000,59b9c5f..d4be512 mode 000000,100644..100644 --- a/src/php_http_options.c +++ b/src/php_http_options.c @@@ -1,0 -1,126 +1,134 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + ++static void php_http_options_hash_dtor(zval *pData) ++{ ++ php_http_option_t *opt = Z_PTR_P(pData); ++ ++ zval_ptr_dtor(&opt->defval); ++ zend_hash_destroy(&opt->suboptions.options); ++ zend_string_release(opt->name); ++ pefree(opt, opt->persistent); ++} ++ + php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent) + { + if (!registry) { + registry = pecalloc(1, sizeof(*registry), persistent); + } else { + memset(registry, 0, sizeof(*registry)); + } + + registry->persistent = persistent; - zend_hash_init(®istry->options, 0, NULL, (dtor_func_t) zend_hash_destroy, persistent); ++ zend_hash_init(®istry->options, 0, NULL, php_http_options_hash_dtor, persistent); + + return registry; + } + + ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata) + { - HashPosition pos; - zval *val; ++ zval *entry, *val; + php_http_option_t *opt; + - FOREACH_HASH_VAL(pos, ®istry->options, opt) { ++ ZEND_HASH_FOREACH_VAL(®istry->options, entry) ++ { ++ opt = Z_PTR_P(entry); + if (!(val = registry->getter(opt, options, userdata))) { + val = &opt->defval; + } + if (registry->setter) { + if (SUCCESS != registry->setter(opt, val, userdata)) { + return FAILURE; + } + } else if (!opt->setter || SUCCESS != opt->setter(opt, val, userdata)) { + return FAILURE; + } + } ++ ZEND_HASH_FOREACH_END(); ++ + return SUCCESS; + } + + void php_http_options_dtor(php_http_options_t *registry) + { + zend_hash_destroy(®istry->options); + } + + void php_http_options_free(php_http_options_t **registry) + { + if (*registry) { + php_http_options_dtor(*registry); + pefree(*registry, (*registry)->persistent); + *registry = NULL; + } + } + + php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type) + { - php_http_option_t opt, *dst = NULL; ++ php_http_option_t opt; + + memset(&opt, 0, sizeof(opt)); + + php_http_options_init(&opt.suboptions, registry->persistent); + opt.suboptions.getter = registry->getter; + opt.suboptions.setter = registry->setter; + - opt.name.h = zend_hash_func(opt.name.s = name_str, opt.name.l = name_len + 1); ++ opt.persistent = registry->persistent; ++ opt.name = zend_string_init(name_str, name_len, registry->persistent); + opt.type = type; + opt.option = option; + - INIT_ZVAL(opt.defval); + switch ((opt.type = type)) { - case IS_BOOL: - ZVAL_BOOL(&opt.defval, 0); ++ case IS_TRUE: ++ ZVAL_TRUE(&opt.defval); + break; + - case IS_LONG: - ZVAL_LONG(&opt.defval, 0); ++ case IS_FALSE: ++ ZVAL_FALSE(&opt.defval); + break; + - case IS_STRING: - ZVAL_STRINGL(&opt.defval, NULL, 0, 0); ++ case IS_LONG: ++ ZVAL_LONG(&opt.defval, 0); + break; + + case IS_DOUBLE: + ZVAL_DOUBLE(&opt.defval, 0); + break; + + default: + ZVAL_NULL(&opt.defval); + break; + } + - zend_hash_quick_update(®istry->options, opt.name.s, opt.name.l, opt.name.h, (void *) &opt, sizeof(opt), (void *) &dst); - return dst; ++ return zend_hash_update_mem(®istry->options, opt.name, &opt, sizeof(opt)); + } + + zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata) + { + if (options) { - zval **zoption; - - if (SUCCESS == zend_hash_quick_find(options, opt->name.s, opt->name.l, opt->name.h, (void *) &zoption)) { - return *zoption; - } ++ return zend_hash_find(options, opt->name); + } + + 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 --cc src/php_http_options.h index 0000000,2475383..4fff611 mode 000000,100644..100644 --- a/src/php_http_options.h +++ b/src/php_http_options.h @@@ -1,0 -1,65 +1,61 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_OPTIONS_H + #define PHP_HTTP_OPTIONS_H + + typedef struct php_http_option php_http_option_t; + typedef struct php_http_options php_http_options_t; + + typedef ZEND_RESULT_CODE (*php_http_option_set_callback_t)(php_http_option_t *opt, zval *val, void *userdata); + typedef zval *(*php_http_option_get_callback_t)(php_http_option_t *opt, HashTable *options, void *userdata); + + struct php_http_options { + HashTable options; + + php_http_option_get_callback_t getter; + php_http_option_set_callback_t setter; + + unsigned persistent:1; + }; + + struct php_http_option { + php_http_options_t suboptions; + - struct { - const char *s; - size_t l; - ulong h; - } name; - ++ zend_string *name; + ulong option; + zend_uchar type; + unsigned flags; + zval defval; + + php_http_option_set_callback_t setter; ++ unsigned persistent:1; + }; + + PHP_HTTP_API php_http_options_t *php_http_options_init(php_http_options_t *registry, zend_bool persistent); + PHP_HTTP_API ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata); + PHP_HTTP_API void php_http_options_dtor(php_http_options_t *registry); + PHP_HTTP_API void php_http_options_free(php_http_options_t **registry); + + PHP_HTTP_API php_http_option_t *php_http_option_register(php_http_options_t *registry, const char *name_str, size_t name_len, ulong option, zend_uchar type); + PHP_HTTP_API zval *php_http_option_get(php_http_option_t *opt, HashTable *options, void *userdata); + + #endif /* PHP_HTTP_OPTIONS_H */ + + /* + * 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 --cc src/php_http_params.c index 0000000,5adeb91..b51ab71 mode 000000,100644..100644 --- a/src/php_http_params.c +++ b/src/php_http_params.c @@@ -1,0 -1,1283 +1,1328 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + static php_http_params_token_t def_param_sep = {",", 1}, *def_param_sep_ptr[] = {&def_param_sep, NULL}; + static php_http_params_token_t def_arg_sep = {";", 1}, *def_arg_sep_ptr[] = {&def_arg_sep, NULL}; + static php_http_params_token_t def_val_sep = {"=", 1}, *def_val_sep_ptr[] = {&def_val_sep, NULL}; + static php_http_params_opts_t def_opts = { + {NULL, 0}, + def_param_sep_ptr, + def_arg_sep_ptr, + def_val_sep_ptr, - NULL, ++ {{0}}, + PHP_HTTP_PARAMS_DEFAULT + }; + + php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts) + { + if (!opts) { + opts = emalloc(sizeof(*opts)); + } + + memcpy(opts, &def_opts, sizeof(def_opts)); + + return opts; + } + + typedef struct php_http_params_state { + php_http_params_token_t input; + php_http_params_token_t param; + php_http_params_token_t arg; + php_http_params_token_t val; + struct { - zval **param; - zval **args; - zval **val; ++ zval *param; ++ zval *args; ++ zval *val; + } current; + unsigned quotes:1; + unsigned escape:1; + unsigned rfc5987:1; + } php_http_params_state_t; + -static inline void sanitize_escaped(zval *zv TSRMLS_DC) ++static inline void sanitize_escaped(zval *zv) + { + if (Z_STRVAL_P(zv)[0] == '"' && Z_STRVAL_P(zv)[Z_STRLEN_P(zv) - 1] == '"') { + size_t deq_len = Z_STRLEN_P(zv) - 2; + char *deq = estrndup(Z_STRVAL_P(zv) + 1, deq_len); + + zval_dtor(zv); - ZVAL_STRINGL(zv, deq, deq_len, 0); ++ ZVAL_STR(zv, php_http_cs2zs(deq, deq_len)); + } + - php_stripcslashes(Z_STRVAL_P(zv), &Z_STRLEN_P(zv)); ++ php_stripcslashes(Z_STR_P(zv)); + } + -static inline void quote_string(zval *zv, zend_bool force TSRMLS_DC) ++static inline void quote_string(zend_string **zs, zend_bool force) + { - int len = Z_STRLEN_P(zv); ++ int len = (*zs)->len; + - Z_STRVAL_P(zv) = php_addcslashes(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &Z_STRLEN_P(zv), 1, - ZEND_STRL("\0..\37\173\\\"") TSRMLS_CC); ++ *zs = php_addcslashes(*zs, 1, ZEND_STRL("\0..\37\173\\\"")); + - if (force || len != Z_STRLEN_P(zv) || strpbrk(Z_STRVAL_P(zv), "()<>@,;:\"[]?={} ")) { - zval tmp = *zv; - int len = Z_STRLEN_P(zv) + 2; - char *str = emalloc(len + 1); ++ if (force || len != (*zs)->len || strpbrk((*zs)->val, "()<>@,;:\"[]?={} ")) { ++ int len = (*zs)->len + 2; + - str[0] = '"'; - memcpy(&str[1], Z_STRVAL_P(zv), Z_STRLEN_P(zv)); - str[len-1] = '"'; - str[len] = '\0'; ++ *zs = zend_string_extend(*zs, len, 0); + - zval_dtor(&tmp); - ZVAL_STRINGL(zv, str, len, 0); ++ memmove(&(*zs)->val[1], (*zs)->val, (*zs)->len); ++ (*zs)->val[0] = '"'; ++ (*zs)->val[len-1] = '"'; ++ (*zs)->val[len] = '\0'; ++ ++ zend_string_forget_hash_val(*zs); + } + } + -static inline void prepare_escaped(zval *zv TSRMLS_DC) ++/* if (Z_TYPE_P(zv) == IS_STRING) { ++ size_t len = Z_STRLEN_P(zv); ++ zend_string *stripped = php_addcslashes(Z_STR_P(zv), 0, ++ ZEND_STRL("\0..\37\173\\\"")); ++ ++ if (len != stripped->len || strpbrk(stripped->val, "()<>@,;:\"[]?={} ")) { ++ size_t len = stripped->len + 2; ++ char *str = emalloc(len + 1); ++ ++ str[0] = '"'; ++ memcpy(&str[1], stripped->val, stripped->len); ++ str[len-1] = '"'; ++ str[len] = '\0'; ++ ++ zval_dtor(zv); ++ zend_string_release(stripped); ++ ZVAL_STR(zv, php_http_cs2zs(str, len)); ++ } else { ++ zval_dtor(zv); ++ ZVAL_STR(zv, stripped); ++ } ++*/ ++ ++static inline void prepare_escaped(zval *zv) + { + if (Z_TYPE_P(zv) == IS_STRING) { - quote_string(zv, 0 TSRMLS_CC); ++ quote_string(&Z_STR_P(zv), 0); + } else { + zval_dtor(zv); + ZVAL_EMPTY_STRING(zv); + } + } + -static inline void sanitize_urlencoded(zval *zv TSRMLS_DC) ++static inline void sanitize_urlencoded(zval *zv) + { + Z_STRLEN_P(zv) = php_raw_url_decode(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + } + -static inline void prepare_urlencoded(zval *zv TSRMLS_DC) ++static inline void prepare_urlencoded(zval *zv) + { - int len; - char *str = php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &len); ++ zend_string *str = php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + + zval_dtor(zv); - ZVAL_STRINGL(zv, str, len, 0); ++ ZVAL_STR(zv, str); + } + -static void sanitize_dimension(zval *zv TSRMLS_DC) ++static void sanitize_dimension(zval *zv) + { - zval *arr = NULL, *tmp = NULL, **cur = NULL; ++ zval arr, tmp, *cur = NULL; + char *var = NULL, *ptr = Z_STRVAL_P(zv), *end = Z_STRVAL_P(zv) + Z_STRLEN_P(zv); + long level = 0; + - MAKE_STD_ZVAL(arr); - array_init(arr); ++ array_init(&arr); + cur = &arr; + + while (ptr < end) { + if (!var) { + var = ptr; + } + + switch (*ptr) { + case '[': + if (++level > PG(max_input_nesting_level)) { + zval_ptr_dtor(&arr); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level)); ++ php_error_docref(NULL, E_WARNING, "Max input nesting level of %ld exceeded", (long) PG(max_input_nesting_level)); + return; + } + if (ptr - var == 0) { + ++var; + break; + } + /* no break */ + + case ']': + - MAKE_STD_ZVAL(tmp); - ZVAL_NULL(tmp); - convert_to_array(*cur); ++ ZVAL_NULL(&tmp); ++ convert_to_array(cur); + + if (ptr - var) { + char chr = *ptr; + *ptr = '\0'; - zend_symtable_update(Z_ARRVAL_PP(cur), var, ptr - var + 1, (void *) &tmp, sizeof(zval *), (void *) &cur); ++ cur = zend_symtable_str_update(Z_ARRVAL_P(cur), var, ptr - var, &tmp); + *ptr = chr; + } else { - zend_hash_next_index_insert(Z_ARRVAL_PP(cur), (void *) &tmp, sizeof(zval *), (void *) &cur); ++ cur = zend_hash_next_index_insert(Z_ARRVAL_P(cur), &tmp); + } + + var = NULL; + break; + } + + ++ptr; + } + - if (zend_hash_num_elements(Z_ARRVAL_P(arr))) { ++ if (zend_hash_num_elements(Z_ARRVAL(arr))) { + zval_dtor(zv); -#if PHP_VERSION_ID >= 50400 - ZVAL_COPY_VALUE(zv, arr); -#else - zv->value = arr->value; - Z_TYPE_P(zv) = Z_TYPE_P(arr); -#endif - FREE_ZVAL(arr); ++ ZVAL_COPY_VALUE(zv, &arr); + } else { + zval_ptr_dtor(&arr); + } + } + -static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC); -static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC); ++static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags); ++static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags); + -static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++static void prepare_dimension(php_http_buffer_t *buf, php_http_buffer_t *keybuf, zval *zvalue, const char *pss, size_t psl, const char *vss, size_t vsl, unsigned flags) + { + HashTable *ht = HASH_OF(zvalue); - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **val; ++ php_http_arrkey_t key; ++ zval *val; + php_http_buffer_t prefix; + - if (!ht->nApplyCount++) { ++ if (!ZEND_HASH_GET_APPLY_COUNT(ht)) { ++ ZEND_HASH_INC_APPLY_COUNT(ht); + php_http_buffer_init(&prefix); + php_http_buffer_append(&prefix, keybuf->data, keybuf->used); + - FOREACH_HASH_KEYVAL(pos, ht, key, val) { - if (key.type == HASH_KEY_IS_STRING && !*key.str) { ++ ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, val) ++ { ++ if (key.key && !*key.key->val) { + /* only public properties */ + continue; + } + + php_http_buffer_appends(&prefix, "["); - if (key.type == HASH_KEY_IS_STRING) { - php_http_buffer_append(&prefix, key.str, key.len - 1); ++ if (key.key) { ++ php_http_buffer_append(&prefix, key.key->val, key.key->len); + } else { - php_http_buffer_appendf(&prefix, "%lu", key.num); ++ php_http_buffer_appendf(&prefix, "%lu", key.h); + } + php_http_buffer_appends(&prefix, "]"); + - if (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT) { - prepare_dimension(buf, &prefix, *val, pss, psl, vss, vsl, flags TSRMLS_CC); ++ if (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT) { ++ prepare_dimension(buf, &prefix, val, pss, psl, vss, vsl, flags); + } else { - zval *cpy = php_http_ztyp(IS_STRING, *val); ++ zend_string *cpy = zval_get_string(val); ++ zval tmp; + - shift_key(buf, prefix.data, prefix.used, pss, psl, flags TSRMLS_CC); - shift_val(buf, cpy, vss, vsl, flags TSRMLS_CC); - zval_ptr_dtor(&cpy); ++ ZVAL_STR(&tmp, cpy); ++ shift_key(buf, prefix.data, prefix.used, pss, psl, flags); ++ shift_val(buf, &tmp, vss, vsl, flags); ++ zend_string_release(cpy); + } + + php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used); + } ++ ZEND_HASH_FOREACH_END(); ++ ZEND_HASH_DEC_APPLY_COUNT(ht); ++ + php_http_buffer_dtor(&prefix); + } - --ht->nApplyCount; + } + -static inline void sanitize_key(unsigned flags, char *str, size_t len, zval *zv, zend_bool *rfc5987 TSRMLS_DC) ++static inline void sanitize_key(unsigned flags, const char *str, size_t len, zval *zv, zend_bool *rfc5987) + { + char *eos; ++ zend_string *zs = zend_string_init(str, len, 0); + + zval_dtor(zv); - php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); ++ ZVAL_STR(zv, php_trim(zs, NULL, 0, 3)); ++ zend_string_release(zs); + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { - sanitize_escaped(zv TSRMLS_CC); ++ sanitize_escaped(zv); + } + + if (!Z_STRLEN_P(zv)) { + return; + } + + eos = &Z_STRVAL_P(zv)[Z_STRLEN_P(zv)-1]; + if (*eos == '*') { + *eos = '\0'; + *rfc5987 = 1; + Z_STRLEN_P(zv) -= 1; + } + + if (flags & PHP_HTTP_PARAMS_URLENCODED) { - sanitize_urlencoded(zv TSRMLS_CC); ++ sanitize_urlencoded(zv); + } + + if (flags & PHP_HTTP_PARAMS_DIMENSION) { - sanitize_dimension(zv TSRMLS_CC); ++ sanitize_dimension(zv); + } + } + -static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 TSRMLS_DC) ++static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1) + { + char *ptr; + + /* examples: + * iso-8850-1'de'bl%f6der%20schei%df%21 + * utf-8'de-DE'bl%c3%b6der%20schei%c3%9f%21 + */ + + switch (Z_STRVAL_P(zv)[0]) { + case 'I': + case 'i': + if (!strncasecmp(Z_STRVAL_P(zv), "iso-8859-1", lenof("iso-8859-1"))) { + *latin1 = 1; + ptr = Z_STRVAL_P(zv) + lenof("iso-8859-1"); + break; + } + /* no break */ + case 'U': + case 'u': + if (!strncasecmp(Z_STRVAL_P(zv), "utf-8", lenof("utf-8"))) { + *latin1 = 0; + ptr = Z_STRVAL_P(zv) + lenof("utf-8"); + break; + } + /* no break */ + default: + return; + } + + /* extract language */ + if (*ptr == '\'') { + for (*language = ++ptr; *ptr && *ptr != '\''; ++ptr); + if (!*ptr) { + *language = NULL; + return; + } + *language = estrndup(*language, ptr - *language); + + /* remainder */ + ptr = estrdup(++ptr); + zval_dtor(zv); - ZVAL_STRING(zv, ptr, 0); ++ ZVAL_STR(zv, php_http_cs2zs(ptr, strlen(ptr))); + } + } + -static inline void sanitize_rfc5988(char *str, size_t len, zval *zv TSRMLS_DC) ++static inline void sanitize_rfc5988(char *str, size_t len, zval *zv) + { ++ zend_string *zs = zend_string_init(str, len, 0); ++ + zval_dtor(zv); - php_trim(str, len, " ><", 3, zv, 3 TSRMLS_CC); ++ ZVAL_STR(zv, php_trim(zs, " ><", 3, 3)); ++ zend_string_release(zs); + } + -static inline void prepare_rfc5988(zval *zv TSRMLS_DC) ++static inline void prepare_rfc5988(zval *zv) + { + if (Z_TYPE_P(zv) != IS_STRING) { + zval_dtor(zv); + ZVAL_EMPTY_STRING(zv); + } + } + + static void utf8encode(zval *zv) + { + size_t pos, len = 0; + unsigned char *ptr = (unsigned char *) Z_STRVAL_P(zv); + + while (*ptr) { + if (*ptr++ >= 0x80) { + ++len; + } + ++len; + } + + ptr = safe_emalloc(1, len, 1); + for (len = 0, pos = 0; len <= Z_STRLEN_P(zv); ++len, ++pos) { + ptr[pos] = Z_STRVAL_P(zv)[len]; + if ((ptr[pos]) >= 0x80) { + ptr[pos + 1] = 0x80 | (ptr[pos] & 0x3f); + ptr[pos] = 0xc0 | ((ptr[pos] >> 6) & 0x1f); + ++pos; + } + } + zval_dtor(zv); - ZVAL_STRINGL(zv, (char *) ptr, pos-1, 0); ++ ZVAL_STR(zv, php_http_cs2zs((char *) ptr, pos-1)); + } + -static inline void sanitize_value(unsigned flags, char *str, size_t len, zval *zv, zend_bool rfc5987 TSRMLS_DC) ++static inline void sanitize_value(unsigned flags, const char *str, size_t len, zval *zv, zend_bool rfc5987) + { + char *language = NULL; + zend_bool latin1 = 0; ++ zend_string *zs = zend_string_init(str, len, 0); + + zval_dtor(zv); - php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); ++ ZVAL_STR(zv, php_trim(zs, NULL, 0, 3)); ++ zend_string_release(zs); + + if (rfc5987) { - sanitize_rfc5987(zv, &language, &latin1 TSRMLS_CC); ++ sanitize_rfc5987(zv, &language, &latin1); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { - sanitize_escaped(zv TSRMLS_CC); ++ sanitize_escaped(zv); + } + + if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) { - sanitize_urlencoded(zv TSRMLS_CC); ++ sanitize_urlencoded(zv); + } + + if (rfc5987 && language) { - zval *tmp; ++ zval tmp; + + if (latin1) { + utf8encode(zv); + } + - MAKE_STD_ZVAL(tmp); - ZVAL_COPY_VALUE(tmp, zv); ++ ZVAL_COPY_VALUE(&tmp, zv); + array_init(zv); - add_assoc_zval(zv, language, tmp); - PTR_FREE(language); ++ add_assoc_zval(zv, language, &tmp); ++ efree(language); + } + } + -static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len TSRMLS_DC) ++static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len) + { + zval zv; + - INIT_PZVAL(&zv); - ZVAL_STRINGL(&zv, old_key, old_len, 1); ++ ZVAL_STRINGL(&zv, old_key, old_len); + + if (flags & PHP_HTTP_PARAMS_URLENCODED) { - prepare_urlencoded(&zv TSRMLS_CC); ++ prepare_urlencoded(&zv); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { + if (flags & PHP_HTTP_PARAMS_RFC5988) { - prepare_rfc5988(&zv TSRMLS_CC); ++ prepare_rfc5988(&zv); + } else { - prepare_escaped(&zv TSRMLS_CC); ++ prepare_escaped(&zv); + } + } + - *new_key = Z_STRVAL(zv); ++ *new_key = estrndup(Z_STRVAL(zv), Z_STRLEN(zv)); + *new_len = Z_STRLEN(zv); ++ zval_ptr_dtor(&zv); + } + -static inline void prepare_value(unsigned flags, zval *zv TSRMLS_DC) ++static inline void prepare_value(unsigned flags, zval *zv) + { + if (flags & PHP_HTTP_PARAMS_URLENCODED) { - prepare_urlencoded(zv TSRMLS_CC); ++ prepare_urlencoded(zv); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { - prepare_escaped(zv TSRMLS_CC); ++ prepare_escaped(zv); + } + } + -static void merge_param(HashTable *params, zval *zdata, zval ***current_param, zval ***current_args TSRMLS_DC) ++static void merge_param(HashTable *params, zval *zdata, zval **current_param, zval **current_args) + { - zval **ptr, **zdata_ptr; - php_http_array_hashkey_t hkey = php_http_array_hashkey_init(0); ++ zval *ptr, *zdata_ptr; ++ php_http_arrkey_t hkey = {0}; + + #if 0 + { + zval tmp; + INIT_PZVAL_ARRAY(&tmp, params); + fprintf(stderr, "params = "); - zend_print_zval_r(&tmp, 1 TSRMLS_CC); ++ zend_print_zval_r(&tmp, 1); + fprintf(stderr, "\n"); + } + #endif + - hkey.type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zdata), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL); ++ zend_hash_get_current_key(Z_ARRVAL_P(zdata), &hkey.key, &hkey.h); + - if ((hkey.type == HASH_KEY_IS_STRING && !zend_hash_exists(params, hkey.str, hkey.len)) - || (hkey.type == HASH_KEY_IS_LONG && !zend_hash_index_exists(params, hkey.num)) ++ if ((hkey.key && !zend_hash_exists(params, hkey.key)) ++ || (!hkey.key && !zend_hash_index_exists(params, hkey.h)) + ) { - zval *tmp, *arg, **args; ++ zval tmp, arg, *args; + + /* create the entry if it doesn't exist */ - zend_hash_get_current_data(Z_ARRVAL_P(zdata), (void *) &ptr); - Z_ADDREF_PP(ptr); - MAKE_STD_ZVAL(tmp); - array_init(tmp); - add_assoc_zval_ex(tmp, ZEND_STRS("value"), *ptr); - - MAKE_STD_ZVAL(arg); - array_init(arg); - zend_hash_update(Z_ARRVAL_P(tmp), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &args); ++ ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata)); ++ Z_TRY_ADDREF_P(ptr); ++ array_init(&tmp); ++ add_assoc_zval_ex(&tmp, ZEND_STRL("value"), ptr); ++ ++ array_init(&arg); ++ args = zend_hash_str_update(Z_ARRVAL(tmp), "arguments", lenof("arguments"), &arg); + *current_args = args; + - if (hkey.type == HASH_KEY_IS_STRING) { - zend_hash_update(params, hkey.str, hkey.len, (void *) &tmp, sizeof(zval *), (void *) &ptr); ++ if (hkey.key) { ++ ptr = zend_hash_update(params, hkey.key, &tmp); + } else { - zend_hash_index_update(params, hkey.num, (void *) &tmp, sizeof(zval *), (void *) &ptr); ++ ptr = zend_hash_index_update(params, hkey.h, &tmp); + } + } else { + /* merge */ - if (hkey.type == HASH_KEY_IS_STRING) { - zend_hash_find(params, hkey.str, hkey.len, (void *) &ptr); ++ if (hkey.key) { ++ ptr = zend_hash_find(params, hkey.key); + } else { - zend_hash_index_find(params, hkey.num, (void *) &ptr); ++ ptr = zend_hash_index_find(params, hkey.h); + } + - zdata_ptr = &zdata; ++ zdata_ptr = zdata; + - if (Z_TYPE_PP(ptr) == IS_ARRAY - && SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), "value", sizeof("value"), (void *) &ptr) - && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &zdata_ptr) ++ if (Z_TYPE_P(ptr) == IS_ARRAY ++ && (ptr = zend_hash_str_find(Z_ARRVAL_P(ptr), "value", lenof("value"))) ++ && (zdata_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr))) + ) { + /* + * params = [arr => [value => [0 => 1]]] + * ^- ptr + * zdata = [arr => [0 => NULL]] + * ^- zdata_ptr + */ - zval **test_ptr; ++ zval *test_ptr; + - while (Z_TYPE_PP(zdata_ptr) == IS_ARRAY - && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(zdata_ptr), (void *) &test_ptr) - ) { - if (Z_TYPE_PP(test_ptr) == IS_ARRAY) { ++ while (Z_TYPE_P(zdata_ptr) == IS_ARRAY && (test_ptr = zend_hash_get_current_data(Z_ARRVAL_P(zdata_ptr)))) { ++ if (Z_TYPE_P(test_ptr) == IS_ARRAY) { ++ zval *tmp_ptr = ptr; + + /* now find key in ptr */ - if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) { - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) &ptr)) { ++ if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) { ++ if ((ptr = zend_hash_find(Z_ARRVAL_P(ptr), hkey.key))) { + zdata_ptr = test_ptr; + } else { - Z_ADDREF_PP(test_ptr); - zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr); ++ ptr = tmp_ptr; ++ Z_TRY_ADDREF_P(test_ptr); ++ ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr); + break; + } + } else { - if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(ptr), hkey.num, (void *) &ptr)) { ++ if ((ptr = zend_hash_index_find(Z_ARRVAL_P(ptr), hkey.h))) { + zdata_ptr = test_ptr; - } else if (hkey.num) { - Z_ADDREF_PP(test_ptr); - zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr); ++ } else if (hkey.h) { ++ ptr = tmp_ptr; ++ Z_TRY_ADDREF_P(test_ptr); ++ ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr); + break; + } else { - Z_ADDREF_PP(test_ptr); - zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr); ++ ptr = tmp_ptr; ++ Z_TRY_ADDREF_P(test_ptr); ++ ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr); + break; + } + } + } else { + /* this is the leaf */ - Z_ADDREF_PP(test_ptr); - if (Z_TYPE_PP(ptr) != IS_ARRAY) { - zval_dtor(*ptr); - array_init(*ptr); ++ Z_TRY_ADDREF_P(test_ptr); ++ if (Z_TYPE_P(ptr) != IS_ARRAY) { ++ zval_dtor(ptr); ++ array_init(ptr); + } - if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_PP(zdata_ptr), &hkey.str, &hkey.len, &hkey.num, hkey.dup, NULL)) { - zend_hash_update(Z_ARRVAL_PP(ptr), hkey.str, hkey.len, (void *) test_ptr, sizeof(zval *), (void *) &ptr); - } else if (hkey.num) { - zend_hash_index_update(Z_ARRVAL_PP(ptr), hkey.num, (void *) test_ptr, sizeof(zval *), (void *) &ptr); ++ if (HASH_KEY_IS_STRING == zend_hash_get_current_key(Z_ARRVAL_P(zdata_ptr), &hkey.key, &hkey.h)) { ++ ptr = zend_hash_update(Z_ARRVAL_P(ptr), hkey.key, test_ptr); ++ } else if (hkey.h) { ++ ptr = zend_hash_index_update(Z_ARRVAL_P(ptr), hkey.h, test_ptr); + } else { - zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr); ++ ptr = zend_hash_next_index_insert(Z_ARRVAL_P(ptr), test_ptr); + } + break; + } + } + + } + } + + /* bubble up */ - while (Z_TYPE_PP(ptr) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(ptr), (void *) &ptr)); ++ while (Z_TYPE_P(ptr) == IS_ARRAY) { ++ zval *tmp = zend_hash_get_current_data(Z_ARRVAL_P(ptr)); ++ ++ if (tmp) { ++ ptr = tmp; ++ } else { ++ break; ++ } ++ } + *current_param = ptr; + } + -static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts TSRMLS_DC) ++static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts) + { + if (state->val.str) { + if (0 < (state->val.len = state->input.str - state->val.str)) { - sanitize_value(opts->flags, state->val.str, state->val.len, *(state->current.val), state->rfc5987 TSRMLS_CC); ++ sanitize_value(opts->flags, state->val.str, state->val.len, state->current.val, state->rfc5987); + } + state->rfc5987 = 0; + } else if (state->arg.str) { + if (0 < (state->arg.len = state->input.str - state->arg.str)) { - zval *val, key; ++ zval val, key; + zend_bool rfc5987 = 0; + - INIT_PZVAL(&key); + ZVAL_NULL(&key); - sanitize_key(opts->flags, state->arg.str, state->arg.len, &key, &rfc5987 TSRMLS_CC); ++ sanitize_key(opts->flags, state->arg.str, state->arg.len, &key, &rfc5987); + state->rfc5987 = rfc5987; + if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) { - MAKE_STD_ZVAL(val); - ZVAL_TRUE(val); ++ ZVAL_TRUE(&val); + + if (rfc5987) { - zval **rfc; ++ zval *rfc; + - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &rfc)) { - zend_symtable_update(Z_ARRVAL_PP(rfc), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val); ++ if ((rfc = zend_hash_str_find(Z_ARRVAL_P(state->current.args), ZEND_STRL("*rfc5987*")))) { ++ state->current.val = zend_symtable_str_update(Z_ARRVAL_P(rfc), Z_STRVAL(key), Z_STRLEN(key), &val); + } else { - zval *tmp; ++ zval tmp; + - MAKE_STD_ZVAL(tmp); - array_init_size(tmp, 1); - zend_symtable_update(Z_ARRVAL_P(tmp), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val); - zend_symtable_update(Z_ARRVAL_PP(state->current.args), ZEND_STRS("*rfc5987*"), (void *) &tmp, sizeof(zval *), NULL); ++ array_init_size(&tmp, 1); ++ state->current.val = zend_symtable_str_update(Z_ARRVAL(tmp), Z_STRVAL(key), Z_STRLEN(key), &val); ++ zend_symtable_str_update(Z_ARRVAL_P(state->current.args), ZEND_STRL("*rfc5987*"), &tmp); + } + } else { - zend_symtable_update(Z_ARRVAL_PP(state->current.args), Z_STRVAL(key), Z_STRLEN(key) + 1, (void *) &val, sizeof(zval *), (void *) &state->current.val); ++ state->current.val = zend_symtable_str_update(Z_ARRVAL_P(state->current.args), Z_STRVAL(key), Z_STRLEN(key), &val); + } + } + zval_dtor(&key); + } + } else if (state->param.str) { + if (0 < (state->param.len = state->input.str - state->param.str)) { - zval *prm, *arg, *val, *key; ++ zval prm, arg, val, key; + zend_bool rfc5987 = 0; + - MAKE_STD_ZVAL(key); - ZVAL_NULL(key); ++ ZVAL_NULL(&key); + if (opts->flags & PHP_HTTP_PARAMS_RFC5988) { - sanitize_rfc5988(state->param.str, state->param.len, key TSRMLS_CC); ++ sanitize_rfc5988(state->param.str, state->param.len, &key); + } else { - sanitize_key(opts->flags, state->param.str, state->param.len, key, &rfc5987 TSRMLS_CC); ++ sanitize_key(opts->flags, state->param.str, state->param.len, &key, &rfc5987); + state->rfc5987 = rfc5987; + } - if (Z_TYPE_P(key) != IS_STRING) { - merge_param(params, key, &state->current.val, &state->current.args TSRMLS_CC); - } else if (Z_STRLEN_P(key)) { - MAKE_STD_ZVAL(prm); - array_init_size(prm, 2); - - MAKE_STD_ZVAL(val); - if (opts->defval) { - ZVAL_COPY_VALUE(val, opts->defval); - zval_copy_ctor(val); ++ if (Z_TYPE(key) == IS_ARRAY) { ++ merge_param(params, &key, &state->current.val, &state->current.args); ++ } else if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) { ++ // FIXME: array_init_size(&prm, 2); ++ array_init(&prm); ++ ++ if (!Z_ISUNDEF(opts->defval)) { ++ ZVAL_COPY_VALUE(&val, &opts->defval); ++ zval_copy_ctor(&val); + } else { - ZVAL_TRUE(val); ++ ZVAL_TRUE(&val); + } + if (rfc5987 && (opts->flags & PHP_HTTP_PARAMS_RFC5987)) { - zend_hash_update(Z_ARRVAL_P(prm), "*rfc5987*", sizeof("*rfc5987*"), (void *) &val, sizeof(zval *), (void *) &state->current.val); ++ state->current.val = zend_hash_str_update(Z_ARRVAL(prm), "*rfc5987*", lenof("*rfc5987*"), &val); + } else { - zend_hash_update(Z_ARRVAL_P(prm), "value", sizeof("value"), (void *) &val, sizeof(zval *), (void *) &state->current.val); ++ state->current.val = zend_hash_str_update(Z_ARRVAL(prm), "value", lenof("value"), &val); + } - - MAKE_STD_ZVAL(arg); - array_init_size(arg, 3); - zend_hash_update(Z_ARRVAL_P(prm), "arguments", sizeof("arguments"), (void *) &arg, sizeof(zval *), (void *) &state->current.args); - - zend_symtable_update(params, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void *) &prm, sizeof(zval *), (void *) &state->current.param); ++ // FIXME: array_init_size(&arg, 3); ++ array_init(&arg); ++ state->current.args = zend_hash_str_update(Z_ARRVAL(prm), "arguments", lenof("arguments"), &arg); ++ state->current.param = zend_symtable_str_update(params, Z_STRVAL(key), Z_STRLEN(key), &prm); + } + zval_ptr_dtor(&key); + } + } + } + + static inline zend_bool check_str(const char *chk_str, size_t chk_len, const char *sep_str, size_t sep_len) { + return 0 < sep_len && chk_len >= sep_len && *chk_str == *sep_str && !memcmp(chk_str + 1, sep_str + 1, sep_len - 1); + } + + static size_t check_sep(php_http_params_state_t *state, php_http_params_token_t **separators) + { + php_http_params_token_t **sep = separators; + + if (state->quotes || state->escape) { + return 0; + } + + if (sep) while (*sep) { + if (check_str(state->input.str, state->input.len, (*sep)->str, (*sep)->len)) { + return (*sep)->len; + } + ++sep; + } + return 0; + } + -static void skip_sep(size_t skip, php_http_params_state_t *state, php_http_params_token_t **param, php_http_params_token_t **arg, php_http_params_token_t **val TSRMLS_DC) ++static void skip_sep(size_t skip, php_http_params_state_t *state, php_http_params_token_t **param, php_http_params_token_t **arg, php_http_params_token_t **val) + { + size_t sep_len; + + state->input.str += skip; + state->input.len -= skip; + + while ( (param && (sep_len = check_sep(state, param))) + || (arg && (sep_len = check_sep(state, arg))) + || (val && (sep_len = check_sep(state, val))) + ) { + state->input.str += sep_len; + state->input.len -= sep_len; + } + } + -HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC) ++HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts) + { + php_http_params_state_t state = {{NULL,0}, {NULL,0}, {NULL,0}, {NULL,0}, {NULL,NULL,NULL}, 0, 0}; + + state.input.str = opts->input.str; + state.input.len = opts->input.len; + + if (!params) { + ALLOC_HASHTABLE(params); + ZEND_INIT_SYMTABLE(params); + } + + while (state.input.len) { + if ((opts->flags & PHP_HTTP_PARAMS_RFC5988) && !state.arg.str) { + if (*state.input.str == '<') { + state.quotes = 1; + } else if (*state.input.str == '>') { + state.quotes = 0; + } + } else if (*state.input.str == '"' && !state.escape) { + state.quotes = !state.quotes; + } else { + state.escape = (*state.input.str == '\\'); + } + + if (!state.param.str) { + /* initialize */ - skip_sep(0, &state, opts->param, opts->arg, opts->val TSRMLS_CC); ++ skip_sep(0, &state, opts->param, opts->arg, opts->val); + state.param.str = state.input.str; + } else { + size_t sep_len; + /* are we at a param separator? */ + if (0 < (sep_len = check_sep(&state, opts->param))) { - push_param(params, &state, opts TSRMLS_CC); ++ push_param(params, &state, opts); + - skip_sep(sep_len, &state, opts->param, opts->arg, opts->val TSRMLS_CC); ++ skip_sep(sep_len, &state, opts->param, opts->arg, opts->val); + + /* start off with a new param */ + state.param.str = state.input.str; + state.param.len = 0; + state.arg.str = NULL; + state.arg.len = 0; + state.val.str = NULL; + state.val.len = 0; + + continue; + + } else + /* are we at an arg separator? */ + if (0 < (sep_len = check_sep(&state, opts->arg))) { - push_param(params, &state, opts TSRMLS_CC); ++ push_param(params, &state, opts); + - skip_sep(sep_len, &state, NULL, opts->arg, opts->val TSRMLS_CC); ++ skip_sep(sep_len, &state, NULL, opts->arg, opts->val); + + /* continue with a new arg */ + state.arg.str = state.input.str; + state.arg.len = 0; + state.val.str = NULL; + state.val.len = 0; + + continue; + + } else + /* are we at a val separator? */ + if (0 < (sep_len = check_sep(&state, opts->val))) { + /* only handle separator if we're not already reading in a val */ + if (!state.val.str) { - push_param(params, &state, opts TSRMLS_CC); ++ push_param(params, &state, opts); + - skip_sep(sep_len, &state, NULL, NULL, opts->val TSRMLS_CC); ++ skip_sep(sep_len, &state, NULL, NULL, opts->val); + + state.val.str = state.input.str; + state.val.len = 0; + + continue; + } + } + } + + if (state.input.len) { + ++state.input.str; + --state.input.len; + } + } + /* finalize */ - push_param(params, &state, opts TSRMLS_CC); ++ push_param(params, &state, opts); + + return params; + } + -static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC) ++static inline void shift_key(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags) + { + char *str; + size_t len; + + if (buf->used) { + php_http_buffer_append(buf, ass, asl); + } + - prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC); ++ prepare_key(flags, key_str, key_len, &str, &len); + php_http_buffer_append(buf, str, len); + efree(str); + } + -static inline void shift_rfc5987(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++static inline void shift_rfc5987(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags) + { + HashTable *ht = HASH_OF(zvalue); - zval **zdata, *tmp; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); ++ zval *zdata, tmp; ++ zend_string *zs; ++ php_http_arrkey_t key = {0}; + - if (SUCCESS == zend_hash_get_current_data(ht, (void *) &zdata) - && HASH_KEY_NON_EXISTENT != (key.type = zend_hash_get_current_key_ex(ht, &key.str, &key.len, &key.num, key.dup, NULL)) ++ if ((zdata = zend_hash_get_current_data(ht)) ++ && HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(ht, &key.key, &key.h) + ) { - php_http_array_hashkey_stringify(&key); ++ php_http_arrkey_stringify(&key, NULL); + php_http_buffer_appendf(buf, "*%.*sutf-8'%.*s'", + (int) (vsl > INT_MAX ? INT_MAX : vsl), vss, - (int) (key.len > INT_MAX ? INT_MAX : key.len), key.str); - php_http_array_hashkey_stringfree(&key); ++ (int) (key.key->len > INT_MAX ? INT_MAX : key.key->len), key.key->val); ++ php_http_arrkey_dtor(&key); + - tmp = php_http_zsep(1, IS_STRING, *zdata); - prepare_value(flags | PHP_HTTP_PARAMS_URLENCODED, tmp TSRMLS_CC); - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); ++ if (Z_TYPE_P(zdata) == IS_INDIRECT) { ++ zdata = Z_INDIRECT_P(zdata); ++ } ++ zs = zval_get_string(zdata); ++ ZVAL_STR(&tmp, zs); ++ prepare_value(flags | PHP_HTTP_PARAMS_URLENCODED, &tmp); ++ php_http_buffer_append(buf, Z_STRVAL(tmp), Z_STRLEN(tmp)); + zval_ptr_dtor(&tmp); + } + } + -static inline void shift_rfc5988(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags TSRMLS_DC) ++static inline void shift_rfc5988(php_http_buffer_t *buf, char *key_str, size_t key_len, const char *ass, size_t asl, unsigned flags) + { + char *str; + size_t len; + + if (buf->used) { + php_http_buffer_append(buf, ass, asl); + } + - prepare_key(flags, key_str, key_len, &str, &len TSRMLS_CC); ++ prepare_key(flags, key_str, key_len, &str, &len); + php_http_buffer_appends(buf, "<"); + php_http_buffer_append(buf, str, len); + php_http_buffer_appends(buf, ">"); + efree(str); + } + -static inline void shift_rfc5988_val(php_http_buffer_t *buf, zval *zv, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++static inline void shift_rfc5988_val(php_http_buffer_t *buf, zval *zv, const char *vss, size_t vsl, unsigned flags) + { - zval *tmp = php_http_zsep(1, IS_STRING, zv); ++ zend_string *zs = zval_get_string(zv); + - quote_string(tmp, 1 TSRMLS_CC); ++ quote_string(&zs, 1); + php_http_buffer_append(buf, vss, vsl); - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); ++ php_http_buffer_append(buf, zs->val, zs->len); + - zval_ptr_dtor(&tmp); ++ zend_string_release(zs); + } + -static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags) + { - if (Z_TYPE_P(zvalue) != IS_BOOL) { - zval *tmp = php_http_zsep(1, IS_STRING, zvalue); ++ zval tmp; ++ zend_string *zs; + - prepare_value(flags, tmp TSRMLS_CC); - php_http_buffer_append(buf, vss, vsl); - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); ++ switch (Z_TYPE_P(zvalue)) { ++ case IS_TRUE: ++ break; + - zval_ptr_dtor(&tmp); - } else if (!Z_BVAL_P(zvalue)) { ++ case IS_FALSE: + php_http_buffer_append(buf, vss, vsl); + php_http_buffer_appends(buf, "0"); ++ break; ++ ++ default: ++ zs = zval_get_string(zvalue); ++ ++ ZVAL_STR(&tmp, zs); ++ prepare_value(flags, &tmp); ++ php_http_buffer_append(buf, vss, vsl); ++ php_http_buffer_append(buf, Z_STRVAL(tmp), Z_STRLEN(tmp)); ++ ++ zval_ptr_dtor(&tmp); ++ break; + } + } + -static void shift_arg(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++static void shift_arg(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags) + { + if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) { - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **val; ++ php_http_arrkey_t key; ++ HashTable *ht = HASH_OF(zvalue); ++ zval *val; + zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*"); + + if (!rfc5987) { - shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); ++ shift_key(buf, key_str, key_len, ass, asl, flags); + } - FOREACH_KEYVAL(pos, zvalue, key, val) { ++ ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, val) ++ { + /* did you mean recursion? */ - php_http_array_hashkey_stringify(&key); - if (rfc5987 && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT)) { - shift_key(buf, key.str, key.len-1, ass, asl, flags TSRMLS_CC); - shift_rfc5987(buf, *val, vss, vsl, flags TSRMLS_CC); ++ php_http_arrkey_stringify(&key, NULL); ++ if (rfc5987 && (Z_TYPE_P(val) == IS_ARRAY || Z_TYPE_P(val) == IS_OBJECT)) { ++ shift_key(buf, key.key->val, key.key->len, ass, asl, flags); ++ shift_rfc5987(buf, val, vss, vsl, flags); + } else { - shift_arg(buf, key.str, key.len-1, *val, ass, asl, vss, vsl, flags TSRMLS_CC); ++ shift_arg(buf, key.key->val, key.key->len, val, ass, asl, vss, vsl, flags); + } - php_http_array_hashkey_stringfree(&key); ++ php_http_arrkey_dtor(&key); + } ++ ZEND_HASH_FOREACH_END(); + } else { - shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); ++ shift_key(buf, key_str, key_len, ass, asl, flags); + + if (flags & PHP_HTTP_PARAMS_RFC5988) { + switch (key_len) { + case lenof("rel"): + case lenof("title"): + case lenof("anchor"): + /* some args must be quoted */ + if (0 <= php_http_select_str(key_str, 3, "rel", "title", "anchor")) { - shift_rfc5988_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); ++ shift_rfc5988_val(buf, zvalue, vss, vsl, flags); + return; + } + break; + } + } + - shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); ++ shift_val(buf, zvalue, vss, vsl, flags); + } + } + -static void shift_param(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags, zend_bool rfc5987 TSRMLS_DC) ++static void shift_param(php_http_buffer_t *buf, char *key_str, size_t key_len, zval *zvalue, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags, zend_bool rfc5987) + { + if (Z_TYPE_P(zvalue) == IS_ARRAY || Z_TYPE_P(zvalue) == IS_OBJECT) { + /* treat as arguments, unless we care for dimensions or rfc5987 */ + if (flags & PHP_HTTP_PARAMS_DIMENSION) { + php_http_buffer_t *keybuf = php_http_buffer_from_string(key_str, key_len); - prepare_dimension(buf, keybuf, zvalue, pss, psl, vss, vsl, flags TSRMLS_CC); ++ prepare_dimension(buf, keybuf, zvalue, pss, psl, vss, vsl, flags); + php_http_buffer_free(&keybuf); + } else if (rfc5987) { - shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); - shift_rfc5987(buf, zvalue, vss, vsl, flags TSRMLS_CC); ++ shift_key(buf, key_str, key_len, pss, psl, flags); ++ shift_rfc5987(buf, zvalue, vss, vsl, flags); + } else { - shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags TSRMLS_CC); ++ shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags); + } + } else { + if (flags & PHP_HTTP_PARAMS_RFC5988) { - shift_rfc5988(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); ++ shift_rfc5988(buf, key_str, key_len, pss, psl, flags); + } else { - shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); ++ shift_key(buf, key_str, key_len, pss, psl, flags); + } - shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); ++ shift_val(buf, zvalue, vss, vsl, flags); + } + } + -php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) ++php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags) + { - zval **zparam; - HashPosition pos, pos1; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0), key1 = php_http_array_hashkey_init(0); ++ zval *zparam; ++ php_http_arrkey_t key; + zend_bool rfc5987 = 0; + + if (!buf) { + buf = php_http_buffer_init(NULL); + } + - FOREACH_HASH_KEYVAL(pos, params, key, zparam) { - zval **zvalue, **zargs; ++ ZEND_HASH_FOREACH_KEY_VAL(params, key.h, key.key, zparam) ++ { ++ zval *zvalue, *zargs; + - if (Z_TYPE_PP(zparam) != IS_ARRAY) { ++ if (Z_TYPE_P(zparam) != IS_ARRAY) { + zvalue = zparam; + } else { - if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("value"), (void *) &zvalue)) { - if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("*rfc5987*"), (void *) &zvalue)) { ++ if (!(zvalue = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("value")))) { ++ if (!(zvalue = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("*rfc5987*")))) { + zvalue = zparam; + } else { + rfc5987 = 1; + } + } + } + - php_http_array_hashkey_stringify(&key); - shift_param(buf, key.str, key.len - 1, *zvalue, pss, psl, ass, asl, vss, vsl, flags, rfc5987 TSRMLS_CC); - php_http_array_hashkey_stringfree(&key); ++ php_http_arrkey_stringify(&key, NULL); ++ shift_param(buf, key.key->val, key.key->len, zvalue, pss, psl, ass, asl, vss, vsl, flags, rfc5987); ++ php_http_arrkey_dtor(&key); ++ ++ if (Z_TYPE_P(zparam) == IS_ARRAY) { ++ zval *tmp = zend_hash_str_find(Z_ARRVAL_P(zparam), ZEND_STRL("arguments")); + - if (Z_TYPE_PP(zparam) == IS_ARRAY && SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("arguments"), (void *) &zvalue)) { - if (zvalue == zparam) { ++ if (tmp) { ++ zvalue = tmp; ++ } else if (zvalue == zparam) { + continue; ++ } else { ++ zvalue = zparam; + } - zvalue = zparam; + } + - if (Z_TYPE_PP(zvalue) == IS_ARRAY) { - FOREACH_KEYVAL(pos1, *zvalue, key1, zargs) { - if (zvalue == zparam && key1.type == HASH_KEY_IS_STRING && !strcmp(key1.str, "value")) { ++ if (Z_TYPE_P(zvalue) == IS_ARRAY) { ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zvalue), key.h, key.key, zargs) ++ { ++ if (zvalue == zparam && key.key && zend_string_equals_literal(key.key, "value")) { + continue; + } + - php_http_array_hashkey_stringify(&key1); - shift_arg(buf, key1.str, key1.len - 1, *zargs, ass, asl, vss, vsl, flags TSRMLS_CC); - php_http_array_hashkey_stringfree(&key1); ++ php_http_arrkey_stringify(&key, NULL); ++ shift_arg(buf, key.key->val, key.key->len, zargs, ass, asl, vss, vsl, flags); ++ php_http_arrkey_dtor(&key); + } ++ ZEND_HASH_FOREACH_END(); + } + } ++ ZEND_HASH_FOREACH_END(); + + php_http_buffer_shrink(buf); + php_http_buffer_fix(buf); + + return buf; + } + -php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC) ++php_http_params_token_t **php_http_params_separator_init(zval *zv) + { - zval **sep; - HashPosition pos; ++ zval *sep, ztmp; + php_http_params_token_t **ret, **tmp; + + if (!zv) { + return NULL; + } + - zv = php_http_ztyp(IS_ARRAY, zv); ++ ZVAL_DUP(&ztmp, zv); ++ zv = &ztmp; ++ convert_to_array(zv); ++ + ret = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(zv)) + 1, sizeof(*ret)); + + tmp = ret; - FOREACH_VAL(pos, zv, sep) { - zval *zt = php_http_ztyp(IS_STRING, *sep); ++ ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), sep) ++ { ++ zend_string *zs = zval_get_string(sep); + - if (Z_STRLEN_P(zt)) { ++ if (zs->len) { + *tmp = emalloc(sizeof(**tmp)); - (*tmp)->str = estrndup(Z_STRVAL_P(zt), (*tmp)->len = Z_STRLEN_P(zt)); ++ (*tmp)->str = estrndup(zs->val, (*tmp)->len = zs->len); + ++tmp; + } - zval_ptr_dtor(&zt); ++ zend_string_release(zs); + } - zval_ptr_dtor(&zv); ++ ZEND_HASH_FOREACH_END(); ++ ++ zval_ptr_dtor(&ztmp); + + *tmp = NULL; + return ret; + } + + void php_http_params_separator_free(php_http_params_token_t **separator) + { + php_http_params_token_t **sep = separator; + if (sep) { + while (*sep) { + PTR_FREE((*sep)->str); + efree(*sep); + ++sep; + } + efree(separator); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams___construct, 0, 0, 0) + ZEND_ARG_INFO(0, params) + ZEND_ARG_INFO(0, param_sep) + ZEND_ARG_INFO(0, arg_sep) + ZEND_ARG_INFO(0, val_sep) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, __construct) + { - zval *zcopy, *zparams = NULL, *param_sep = NULL, *arg_sep = NULL, *val_sep = NULL; - long flags = PHP_HTTP_PARAMS_DEFAULT; ++ zval *zparams = NULL, *param_sep = NULL, *arg_sep = NULL, *val_sep = NULL; ++ zend_long flags = PHP_HTTP_PARAMS_DEFAULT; + zend_error_handling zeh; ++ zend_string *zs; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!/z/z/z/l", &zparams, ¶m_sep, &arg_sep, &val_sep, &flags), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!/z/z/z/l", &zparams, ¶m_sep, &arg_sep, &val_sep, &flags), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh); + { + switch (ZEND_NUM_ARGS()) { + case 5: - zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags TSRMLS_CC); ++ zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags); + /* no break */ + case 4: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep); + /* no break */ + case 3: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep); + /* no break */ + case 2: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep); + /* no break */ + } + + if (zparams) { + switch (Z_TYPE_P(zparams)) { + case IS_OBJECT: + case IS_ARRAY: - zcopy = php_http_zsep(1, IS_ARRAY, zparams); - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zcopy TSRMLS_CC); - zval_ptr_dtor(&zcopy); ++ convert_to_array(zparams); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams); + break; + default: - zcopy = php_http_ztyp(IS_STRING, zparams); - if (Z_STRLEN_P(zcopy)) { ++ zs = zval_get_string(zparams); ++ if (zs->len) { ++ zval tmp; ++ + php_http_params_opts_t opts = { - {Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)}, - php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC) TSRMLS_CC), - php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC) TSRMLS_CC), - php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC) TSRMLS_CC), - NULL, flags ++ {zs->val, zs->len}, ++ php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0, &tmp)), ++ php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0, &tmp)), ++ php_http_params_separator_init(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0, &tmp)), ++ {{0}}, flags + }; + - MAKE_STD_ZVAL(zparams); - array_init(zparams); - php_http_params_parse(Z_ARRVAL_P(zparams), &opts TSRMLS_CC); - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC); - zval_ptr_dtor(&zparams); ++ array_init(&tmp); ++ php_http_params_parse(Z_ARRVAL(tmp), &opts); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), &tmp); ++ zval_ptr_dtor(&tmp); + + php_http_params_separator_free(opts.param); + php_http_params_separator_free(opts.arg); + php_http_params_separator_free(opts.val); + } - zval_ptr_dtor(&zcopy); ++ zend_string_release(zs); + break; + } + } else { - MAKE_STD_ZVAL(zparams); - array_init(zparams); - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC); - zval_ptr_dtor(&zparams); ++ zval tmp; ++ ++ array_init(&tmp); ++ zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), &tmp); ++ zval_ptr_dtor(&tmp); + } + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, toArray) + { - zval *zparams; ++ zval zparams_tmp, *zparams; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } - zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); + RETURN_ZVAL(zparams, 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toString, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, toString) + { - zval **tmp, *zparams, *zpsep, *zasep, *zvsep, *zflags; ++ zval *tmp, *zparams, *zpsep, *zasep, *zvsep; ++ zval zparams_tmp, flags_tmp, psep_tmp, asep_tmp, vsep_tmp; ++ zend_string *psep, *asep, *vsep; ++ long flags; + php_http_buffer_t buf; + - zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); - zflags = php_http_ztyp(IS_LONG, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), 0 TSRMLS_CC)); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); ++ convert_to_array_ex(zparams); ++ flags = zval_get_long(zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), 0, &flags_tmp)); + - zpsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0 TSRMLS_CC); - if (Z_TYPE_P(zpsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zpsep), (void *) &tmp)) { - zpsep = php_http_ztyp(IS_STRING, *tmp); ++ zpsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), 0, &psep_tmp); ++ if (Z_TYPE_P(zpsep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zpsep)))) { ++ psep = zval_get_string(tmp); + } else { - zpsep = php_http_ztyp(IS_STRING, zpsep); ++ psep = zval_get_string(zpsep); + } - zasep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0 TSRMLS_CC); - if (Z_TYPE_P(zasep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zasep), (void *) &tmp)) { - zasep = php_http_ztyp(IS_STRING, *tmp); ++ zasep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), 0, &asep_tmp); ++ if (Z_TYPE_P(zasep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zasep)))) { ++ asep = zval_get_string(tmp); + } else { - zasep = php_http_ztyp(IS_STRING, zasep); ++ asep = zval_get_string(zasep); + } - zvsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0 TSRMLS_CC); - if (Z_TYPE_P(zvsep) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(zvsep), (void *) &tmp)) { - zvsep = php_http_ztyp(IS_STRING, *tmp); ++ zvsep = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), 0, &vsep_tmp); ++ if (Z_TYPE_P(zvsep) == IS_ARRAY && (tmp = zend_hash_get_current_data(Z_ARRVAL_P(zvsep)))) { ++ vsep = zval_get_string(tmp); + } else { - zvsep = php_http_ztyp(IS_STRING, zvsep); ++ vsep = zval_get_string(zvsep); + } + + php_http_buffer_init(&buf); - php_http_params_to_string(&buf, Z_ARRVAL_P(zparams), Z_STRVAL_P(zpsep), Z_STRLEN_P(zpsep), Z_STRVAL_P(zasep), Z_STRLEN_P(zasep), Z_STRVAL_P(zvsep), Z_STRLEN_P(zvsep), Z_LVAL_P(zflags) TSRMLS_CC); ++ php_http_params_to_string(&buf, Z_ARRVAL_P(zparams), psep->val, psep->len, asep->val, asep->len, vsep->val, vsep->len, flags); + - zval_ptr_dtor(&zparams); - zval_ptr_dtor(&zpsep); - zval_ptr_dtor(&zasep); - zval_ptr_dtor(&zvsep); - zval_ptr_dtor(&zflags); ++ zend_string_release(psep); ++ zend_string_release(asep); ++ zend_string_release(vsep); + - RETVAL_PHP_HTTP_BUFFER_VAL(&buf); ++ RETVAL_STR(php_http_cs2zs(buf.data, buf.used)); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetExists, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, offsetExists) + { - char *name_str; - int name_len; - zval **zparam, *zparams; ++ zend_string *name; ++ zval zparams_tmp, *zparam, *zparams; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) { + return; + } + - zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); + - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - RETVAL_BOOL(Z_TYPE_PP(zparam) != IS_NULL); ++ if (Z_TYPE_P(zparams) == IS_ARRAY && (zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) { ++ RETVAL_BOOL(Z_TYPE_P(zparam) != IS_NULL); + } else { + RETVAL_FALSE; + } - zval_ptr_dtor(&zparams); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetGet, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, offsetGet) + { - char *name_str; - int name_len; - zval **zparam, *zparams; ++ zend_string *name; ++ zval zparams_tmp, *zparam, *zparams; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) { + return; + } + - zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); + - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - RETVAL_ZVAL(*zparam, 1, 0); ++ if (Z_TYPE_P(zparams) == IS_ARRAY && (zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) { ++ RETVAL_ZVAL(zparam, 1, 0); + } - - zval_ptr_dtor(&zparams); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetUnset, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, offsetUnset) + { - char *name_str; - int name_len; - zval *zparams; ++ zend_string *name; ++ zval zparams_tmp, *zparams; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name)) { + return; + } + - zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); + - zend_symtable_del(Z_ARRVAL_P(zparams), name_str, name_len + 1); - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC); - - zval_ptr_dtor(&zparams); ++ if (Z_TYPE_P(zparams) == IS_ARRAY) { ++ zend_symtable_del(Z_ARRVAL_P(zparams), name); ++ } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_offsetSet, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpParams, offsetSet) + { - zval *nvalue; - char *name_str; - int name_len; - zval **zparam, *zparams; ++ zend_string *name; ++ zval zparams_tmp, *zparam, *zparams, *nvalue; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &nvalue)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &name, &nvalue)) { + return; + } + - zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); ++ zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0, &zparams_tmp); ++ convert_to_array(zparams); + - if (name_len) { ++ if (name->len) { + if (Z_TYPE_P(nvalue) == IS_ARRAY) { - zval *new_zparam; - - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - new_zparam = php_http_zsep(1, IS_ARRAY, *zparam); - array_join(Z_ARRVAL_P(nvalue), Z_ARRVAL_P(new_zparam), 0, 0); ++ if ((zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) { ++ convert_to_array(zparam); ++ array_join(Z_ARRVAL_P(nvalue), Z_ARRVAL_P(zparam), 0, 0); + } else { - new_zparam = nvalue; - Z_ADDREF_P(new_zparam); ++ Z_TRY_ADDREF_P(nvalue); ++ add_assoc_zval_ex(zparams, name->val, name->len, nvalue); + } - add_assoc_zval_ex(zparams, name_str, name_len + 1, new_zparam); + } else { - zval *tmp; ++ zval tmp; + - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - tmp = php_http_zsep(1, IS_ARRAY, *zparam); ++ if ((zparam = zend_symtable_find(Z_ARRVAL_P(zparams), name))) { ++ ZVAL_DUP(&tmp, zparam); ++ convert_to_array(&tmp); + } else { - MAKE_STD_ZVAL(tmp); - array_init(tmp); ++ array_init(&tmp); + } + - Z_ADDREF_P(nvalue); - add_assoc_zval_ex(tmp, ZEND_STRS("value"), nvalue); - add_assoc_zval_ex(zparams, name_str, name_len + 1, tmp); ++ Z_TRY_ADDREF_P(nvalue); ++ add_assoc_zval_ex(&tmp, ZEND_STRL("value"), nvalue); ++ add_assoc_zval_ex(zparams, name->val, name->len, &tmp); + } + } else { - zval *tmp = php_http_ztyp(IS_STRING, nvalue), *arr; ++ zval arr; ++ zend_string *zs = zval_get_string(nvalue); + - MAKE_STD_ZVAL(arr); - array_init(arr); - add_assoc_bool_ex(arr, ZEND_STRS("value"), 1); - add_assoc_zval_ex(zparams, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1, arr); - zval_ptr_dtor(&tmp); ++ array_init(&arr); ++ add_assoc_bool_ex(&arr, ZEND_STRL("value"), 1); ++ add_assoc_zval_ex(zparams, zs->val, zs->len, &arr); ++ zend_string_release(zs); + } - - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), zparams TSRMLS_CC); - zval_ptr_dtor(&zparams); + } + + static zend_function_entry php_http_params_methods[] = { + PHP_ME(HttpParams, __construct, ai_HttpParams___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL) + + PHP_ME(HttpParams, toArray, ai_HttpParams_toArray, ZEND_ACC_PUBLIC) + PHP_ME(HttpParams, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpParams, __toString, toString, ai_HttpParams_toString, ZEND_ACC_PUBLIC) + + PHP_ME(HttpParams, offsetExists, ai_HttpParams_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(HttpParams, offsetUnset, ai_HttpParams_offsetUnset, ZEND_ACC_PUBLIC) + PHP_ME(HttpParams, offsetSet, ai_HttpParams_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(HttpParams, offsetGet, ai_HttpParams_offsetGet, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_params_class_entry; + + PHP_MINIT_FUNCTION(http_params) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Params", php_http_params_methods); - php_http_params_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_params_class_entry = zend_register_internal_class(&ce); + php_http_params_class_entry->create_object = php_http_params_object_new; - zend_class_implements(php_http_params_class_entry TSRMLS_CC, 1, zend_ce_arrayaccess); - - zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(",") TSRMLS_CC); - zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";") TSRMLS_CC); - zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("=") TSRMLS_CC); - zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL("") TSRMLS_CC); - - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_PARAMS_RAW TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_ESCAPED"), PHP_HTTP_PARAMS_ESCAPED TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_URLENCODED"), PHP_HTTP_PARAMS_URLENCODED TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DIMENSION"), PHP_HTTP_PARAMS_DIMENSION TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5987"), PHP_HTTP_PARAMS_RFC5987 TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5988"), PHP_HTTP_PARAMS_RFC5988 TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DEFAULT"), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC); - zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_QUERY"), PHP_HTTP_PARAMS_QUERY TSRMLS_CC); - - zend_declare_property_null(php_http_params_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_long(php_http_params_class_entry, ZEND_STRL("flags"), PHP_HTTP_PARAMS_DEFAULT, ZEND_ACC_PUBLIC TSRMLS_CC); ++ zend_class_implements(php_http_params_class_entry, 1, zend_ce_arrayaccess); ++ ++ zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_PARAM_SEP"), ZEND_STRL(",")); ++ zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_ARG_SEP"), ZEND_STRL(";")); ++ zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("DEF_VAL_SEP"), ZEND_STRL("=")); ++ zend_declare_class_constant_stringl(php_http_params_class_entry, ZEND_STRL("COOKIE_PARAM_SEP"), ZEND_STRL("")); ++ ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RAW"), PHP_HTTP_PARAMS_RAW); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_ESCAPED"), PHP_HTTP_PARAMS_ESCAPED); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_URLENCODED"), PHP_HTTP_PARAMS_URLENCODED); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DIMENSION"), PHP_HTTP_PARAMS_DIMENSION); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5987"), PHP_HTTP_PARAMS_RFC5987); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_RFC5988"), PHP_HTTP_PARAMS_RFC5988); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_DEFAULT"), PHP_HTTP_PARAMS_DEFAULT); ++ zend_declare_class_constant_long(php_http_params_class_entry, ZEND_STRL("PARSE_QUERY"), PHP_HTTP_PARAMS_QUERY); ++ ++ zend_declare_property_null(php_http_params_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC); ++ zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("param_sep"), ZEND_STRL(","), ZEND_ACC_PUBLIC); ++ zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("arg_sep"), ZEND_STRL(";"), ZEND_ACC_PUBLIC); ++ zend_declare_property_stringl(php_http_params_class_entry, ZEND_STRL("val_sep"), ZEND_STRL("="), ZEND_ACC_PUBLIC); ++ zend_declare_property_long(php_http_params_class_entry, ZEND_STRL("flags"), PHP_HTTP_PARAMS_DEFAULT, ZEND_ACC_PUBLIC); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_params.h index 0000000,e1ebe27..b889210 mode 000000,100644..100644 --- a/src/php_http_params.h +++ b/src/php_http_params.h @@@ -1,0 -1,65 +1,65 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_PARAMS_H + #define PHP_HTTP_PARAMS_H + + typedef struct php_http_params_token { + char *str; + size_t len; + } php_http_params_token_t; + + #define PHP_HTTP_PARAMS_RAW 0x00 + #define PHP_HTTP_PARAMS_ESCAPED 0x01 + #define PHP_HTTP_PARAMS_URLENCODED 0x04 + #define PHP_HTTP_PARAMS_DIMENSION 0x08 + #define PHP_HTTP_PARAMS_RFC5987 0x10 + #define PHP_HTTP_PARAMS_RFC5988 0x20 + #define PHP_HTTP_PARAMS_QUERY (PHP_HTTP_PARAMS_URLENCODED|PHP_HTTP_PARAMS_DIMENSION) + #define PHP_HTTP_PARAMS_DEFAULT (PHP_HTTP_PARAMS_ESCAPED|PHP_HTTP_PARAMS_RFC5987) + + typedef struct php_http_params_opts { + php_http_params_token_t input; + php_http_params_token_t **param; + php_http_params_token_t **arg; + php_http_params_token_t **val; - zval *defval; ++ zval defval; + unsigned flags; + } php_http_params_opts_t; + + PHP_HTTP_API php_http_params_opts_t *php_http_params_opts_default_get(php_http_params_opts_t *opts); -PHP_HTTP_API HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts TSRMLS_DC); -PHP_HTTP_API php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags TSRMLS_DC); ++PHP_HTTP_API HashTable *php_http_params_parse(HashTable *params, const php_http_params_opts_t *opts); ++PHP_HTTP_API php_http_buffer_t *php_http_params_to_string(php_http_buffer_t *buf, HashTable *params, const char *pss, size_t psl, const char *ass, size_t asl, const char *vss, size_t vsl, unsigned flags); + -PHP_HTTP_API php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC); ++PHP_HTTP_API php_http_params_token_t **php_http_params_separator_init(zval *zv); + PHP_HTTP_API void php_http_params_separator_free(php_http_params_token_t **separator); + + typedef php_http_object_t php_http_params_object_t; + + PHP_HTTP_API zend_class_entry *php_http_params_class_entry; + + PHP_MINIT_FUNCTION(http_params); + + #define php_http_params_object_new php_http_object_new + #define php_http_params_object_new_ex php_http_object_new_ex + + #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 --cc src/php_http_querystring.c index 0000000,d72337f..5f4eff8 mode 000000,100644..100644 --- a/src/php_http_querystring.c +++ b/src/php_http_querystring.c @@@ -1,0 -1,740 +1,727 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #include + #include + + #ifdef PHP_HTTP_HAVE_ICONV + # ifndef HAVE_ICONV + # define HAVE_ICONV 1 + # endif + # undef PHP_ATOM_INC + # include + #endif + + #define QS_MERGE 1 + -static inline void php_http_querystring_set(zval *instance, zval *params, int flags TSRMLS_DC) ++static inline void php_http_querystring_set(zval *instance, zval *params, int flags) + { - zval *qa; ++ zval qa; ++ ++ array_init(&qa); + + if (flags & QS_MERGE) { - qa = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC)); - } else { - MAKE_STD_ZVAL(qa); - array_init(qa); ++ zval old_tmp, *old = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0, &old_tmp); ++ ++ ZVAL_DEREF(old); ++ if (Z_TYPE_P(old) == IS_ARRAY) { ++ array_copy(Z_ARRVAL_P(old), Z_ARRVAL(qa)); ++ } + } + - php_http_querystring_update(qa, params, NULL TSRMLS_CC); - zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), qa TSRMLS_CC); ++ php_http_querystring_update(&qa, params, NULL); ++ zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), &qa); + zval_ptr_dtor(&qa); + } + -static inline void php_http_querystring_str(zval *instance, zval *return_value TSRMLS_DC) ++static inline void php_http_querystring_str(zval *instance, zval *return_value) + { - zval *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ zval qa_tmp, *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0, &qa_tmp); + ++ ZVAL_DEREF(qa); + if (Z_TYPE_P(qa) == IS_ARRAY) { - php_http_querystring_update(qa, NULL, return_value TSRMLS_CC); ++ php_http_querystring_update(qa, NULL, return_value); + } else { + RETURN_EMPTY_STRING(); + } + } + -static inline void php_http_querystring_get(zval *this_ptr, int type, char *name, uint name_len, zval *defval, zend_bool del, zval *return_value TSRMLS_DC) ++static inline void php_http_querystring_get(zval *instance, int type, char *name, uint name_len, zval *defval, zend_bool del, zval *return_value) + { - zval **arrval, *qarray = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ zval *arrval, qarray_tmp, *qarray = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0, &qarray_tmp); ++ ++ ZVAL_DEREF(qarray); ++ if ((Z_TYPE_P(qarray) == IS_ARRAY) && (arrval = zend_symtable_str_find(Z_ARRVAL_P(qarray), name, name_len))) { ++ if (type && type != Z_TYPE_P(arrval)) { ++ zval tmp; + - if ((Z_TYPE_P(qarray) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qarray), name, name_len + 1, (void *) &arrval))) { - if (type) { - zval *value = php_http_ztyp(type, *arrval); - RETVAL_ZVAL(value, 1, 1); ++ ZVAL_DUP(&tmp, arrval); ++ convert_to_explicit_type(&tmp, type); ++ RETVAL_ZVAL(&tmp, 0, 0); + } else { - RETVAL_ZVAL(*arrval, 1, 0); ++ RETVAL_ZVAL(arrval, 1, 0); + } + + if (del) { - zval *delarr; ++ zval delarr; + - MAKE_STD_ZVAL(delarr); - array_init(delarr); - add_assoc_null_ex(delarr, name, name_len + 1); - php_http_querystring_set(this_ptr, delarr, QS_MERGE TSRMLS_CC); ++ array_init(&delarr); ++ add_assoc_null_ex(&delarr, name, name_len); ++ php_http_querystring_set(instance, &delarr, QS_MERGE); + zval_ptr_dtor(&delarr); + } + } else if(defval) { + RETURN_ZVAL(defval, 1, 0); + } + } + + #ifdef PHP_HTTP_HAVE_ICONV -ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC) ++ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe) + { - HashPosition pos; - zval **entry = NULL; - char *xlate_str = NULL, *xkey; - size_t xlate_len = 0, xlen; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); ++ zval *entry; ++ zend_string *xkey, *xstr; ++ php_http_arrkey_t key; + - FOREACH_KEYVAL(pos, src, key, entry) { - if (key.type == HASH_KEY_IS_STRING) { - if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(key.str, key.len-1, &xkey, &xlen, oe, ie)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", key.len-1, key.str, ie, oe); ++ ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(src), key.h, key.key, entry) ++ { ++ if (key.key) { ++ if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(key.key->val, key.key->len, &xkey, oe, ie)) { ++ php_error_docref(NULL, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", key.key->len, key.key->val, ie, oe); + return FAILURE; + } + } + - if (Z_TYPE_PP(entry) == IS_STRING) { - if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(Z_STRVAL_PP(entry), Z_STRLEN_PP(entry), &xlate_str, &xlate_len, oe, ie)) { - if (key.type == HASH_KEY_IS_STRING) { - efree(xkey); ++ if (Z_TYPE_P(entry) == IS_STRING) { ++ if (PHP_ICONV_ERR_SUCCESS != php_iconv_string(Z_STRVAL_P(entry), Z_STRLEN_P(entry), &xstr, oe, ie)) { ++ if (key.key) { ++ zend_string_release(xkey); + } - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", Z_STRLEN_PP(entry), Z_STRVAL_PP(entry), ie, oe); ++ php_error_docref(NULL, E_WARNING, "Failed to convert '%.*s' from '%s' to '%s'", Z_STRLEN_P(entry), Z_STRVAL_P(entry), ie, oe); + return FAILURE; + } - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_stringl_ex(dst, xkey, xlen+1, xlate_str, xlate_len, 0); ++ if (key.key) { ++ add_assoc_str_ex(dst, xkey->val, xkey->len, xstr); + } else { - add_index_stringl(dst, key.num, xlate_str, xlate_len, 0); ++ add_index_str(dst, key.h, xstr); + } - } else if (Z_TYPE_PP(entry) == IS_ARRAY) { - zval *subarray; ++ } else if (Z_TYPE_P(entry) == IS_ARRAY) { ++ zval subarray; + - MAKE_STD_ZVAL(subarray); - array_init(subarray); - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_zval_ex(dst, xkey, xlen+1, subarray); ++ array_init(&subarray); ++ if (key.key) { ++ add_assoc_zval_ex(dst, xkey->val, xkey->len, &subarray); + } else { - add_index_zval(dst, key.num, subarray); ++ add_index_zval(dst, key.h, &subarray); + } - if (SUCCESS != php_http_querystring_xlate(subarray, *entry, ie, oe TSRMLS_CC)) { - if (key.type == HASH_KEY_IS_STRING) { - efree(xkey); ++ if (SUCCESS != php_http_querystring_xlate(&subarray, entry, ie, oe)) { ++ if (key.key) { ++ zend_string_release(xkey); + } + return FAILURE; + } + } + - if (key.type == HASH_KEY_IS_STRING) { - efree(xkey); ++ if (key.key) { ++ zend_string_release(xkey); + } + } ++ ZEND_HASH_FOREACH_END(); ++ + return SUCCESS; + } + #endif /* HAVE_ICONV */ + -ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC) ++ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params) + { - php_http_querystring_set(instance, params, 0 TSRMLS_CC); ++ php_http_querystring_set(instance, params, 0); + return SUCCESS; + } + -static int apply_querystring(void *pData TSRMLS_DC) ++static int apply_querystring(zval *val) + { - zval **val = pData; ++ if (Z_TYPE_P(val) == IS_ARRAY) { ++ zval *zvalue; + - if (Z_TYPE_PP(val) == IS_ARRAY) { - zval **zvalue; ++ if ((zvalue = zend_hash_str_find(Z_ARRVAL_P(val), ZEND_STRL("value")))) { ++ zval tmp; + - if (SUCCESS == zend_hash_find(Z_ARRVAL_PP(val), ZEND_STRS("value"), (void *) &zvalue)) { - zval *tmp = *val; - - Z_ADDREF_PP(zvalue); - *val = *zvalue; - zval_dtor(tmp); - Z_TYPE_P(tmp) = IS_NULL; - zval_ptr_dtor(&tmp); ++ ZVAL_COPY(&tmp, zvalue); ++ zval_dtor(val); ++ ZVAL_COPY_VALUE(val, &tmp); + } + } + + return ZEND_HASH_APPLY_KEEP; + } + -ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len TSRMLS_DC) ++ZEND_RESULT_CODE php_http_querystring_parse(HashTable *ht, const char *str, size_t len) + { + ZEND_RESULT_CODE rv = FAILURE; + php_http_params_opts_t opts; + php_http_params_token_t psep = { ZEND_STRL("&") }, *psepp[] = { &psep, NULL }; + php_http_params_token_t vsep = { ZEND_STRL("=") }, *vsepp[] = { &vsep, NULL }; + const char *asi_str = NULL; + size_t asi_len = 0; + + opts.input.str = estrndup(str, len); + opts.input.len = len; + opts.param = psepp; + opts.arg = NULL; + opts.val = vsepp; + opts.flags = PHP_HTTP_PARAMS_QUERY; + - if (SUCCESS == php_http_ini_entry(ZEND_STRL("arg_separator.input"), &asi_str, &asi_len, 0 TSRMLS_CC) && asi_len) { - zval *arr; ++ if (SUCCESS == php_http_ini_entry(ZEND_STRL("arg_separator.input"), &asi_str, &asi_len, 0) && asi_len) { ++ zval arr; + - MAKE_STD_ZVAL(arr); - array_init_size(arr, asi_len); ++ array_init_size(&arr, asi_len); + + do { - add_next_index_stringl(arr, asi_str++, 1, 1); ++ add_next_index_stringl(&arr, asi_str++, 1); + } while (*asi_str); + - opts.param = php_http_params_separator_init(arr TSRMLS_CC); - ++ opts.param = php_http_params_separator_init(&arr); + zval_ptr_dtor(&arr); + } + - MAKE_STD_ZVAL(opts.defval); - ZVAL_NULL(opts.defval); ++ ZVAL_NULL(&opts.defval); + - if (php_http_params_parse(ht, &opts TSRMLS_CC)) { - zend_hash_apply(ht, apply_querystring TSRMLS_CC); ++ if (php_http_params_parse(ht, &opts)) { ++ zend_hash_apply(ht, apply_querystring); + rv = SUCCESS; + } + + if (asi_len) { + php_http_params_separator_free(opts.param); + } + + zval_ptr_dtor(&opts.defval); + efree(opts.input.str); + return rv; + } + -ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *outstring TSRMLS_DC) ++ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *outstring) + { + /* enforce proper type */ + if (Z_TYPE_P(qarray) != IS_ARRAY) { + convert_to_array(qarray); + } + + /* modify qarray */ + if (params) { - HashPosition pos; - HashTable *ptr; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **params_entry, **qarray_entry; - zval zv, *zv_ptr = NULL; ++ HashTable *ht; ++ php_http_arrkey_t key; ++ zval zv, *params_entry, *qarray_entry; + - INIT_PZVAL(&zv); + ZVAL_NULL(&zv); + + /* squeeze the hash out of the zval */ - if (Z_TYPE_P(params) == IS_OBJECT && instanceof_function(Z_OBJCE_P(params), php_http_querystring_class_entry TSRMLS_CC)) { - zv_ptr = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, params, ZEND_STRL("queryArray"), 0 TSRMLS_CC)); - ptr = Z_ARRVAL_P(zv_ptr); ++ if (Z_TYPE_P(params) == IS_OBJECT && instanceof_function(Z_OBJCE_P(params), php_http_querystring_class_entry)) { ++ zval qa_tmp, *qa = zend_read_property(php_http_querystring_class_entry, params, ZEND_STRL("queryArray"), 0, &qa_tmp); ++ ++ ZVAL_DEREF(qa); ++ convert_to_array(qa); ++ ht = Z_ARRVAL_P(qa); + } else if (Z_TYPE_P(params) == IS_OBJECT || Z_TYPE_P(params) == IS_ARRAY) { - ptr = HASH_OF(params); ++ ht = HASH_OF(params); + } else { - zv_ptr = php_http_ztyp(IS_STRING, params); ++ zend_string *zs = zval_get_string(params); ++ + array_init(&zv); - php_http_querystring_parse(Z_ARRVAL(zv), Z_STRVAL_P(zv_ptr), Z_STRLEN_P(zv_ptr) TSRMLS_CC); - zval_ptr_dtor(&zv_ptr); - zv_ptr = NULL; - ptr = Z_ARRVAL(zv); ++ php_http_querystring_parse(Z_ARRVAL(zv), zs->val, zs->len); ++ zend_string_release(zs); ++ ++ ht = Z_ARRVAL(zv); + } + - FOREACH_HASH_KEYVAL(pos, ptr, key, params_entry) { ++ ZEND_HASH_FOREACH_KEY_VAL_IND(ht, key.h, key.key, params_entry) ++ { + /* only public properties */ - if (key.type != HASH_KEY_IS_STRING || *key.str) { - if (Z_TYPE_PP(params_entry) == IS_NULL) { ++ if (!key.key || *key.key->val) { ++ if (Z_TYPE_P(params_entry) == IS_NULL) { + /* + * delete + */ - if (key.type == HASH_KEY_IS_STRING) { - zend_hash_del(Z_ARRVAL_P(qarray), key.str, key.len); ++ if (key.key) { ++ zend_hash_del(Z_ARRVAL_P(qarray), key.key); + } else { - zend_hash_index_del(Z_ARRVAL_P(qarray), key.num); ++ zend_hash_index_del(Z_ARRVAL_P(qarray), key.h); + } - } else if ( ((key.type == HASH_KEY_IS_STRING) && (SUCCESS == zend_hash_find(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &qarray_entry))) - || ((key.type == HASH_KEY_IS_LONG) && (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(qarray), key.num, (void *) &qarray_entry)))) { ++ } else if ( ((key.key) && (qarray_entry = zend_hash_find(Z_ARRVAL_P(qarray), key.key))) ++ || ((!key.key) && (qarray_entry = zend_hash_index_find(Z_ARRVAL_P(qarray), key.h)))) { + /* + * update + */ - zval equal, *entry = NULL; ++ zval equal, tmp, *entry = &tmp; + ++ ZVAL_UNDEF(&tmp); + /* recursive */ - if (Z_TYPE_PP(params_entry) == IS_ARRAY || Z_TYPE_PP(params_entry) == IS_OBJECT) { - entry = php_http_zsep(1, IS_ARRAY, *qarray_entry); - php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC); - } else if ((FAILURE == is_equal_function(&equal, *qarray_entry, *params_entry TSRMLS_CC)) || !Z_BVAL(equal)) { - Z_ADDREF_PP(params_entry); - entry = *params_entry; ++ if (Z_TYPE_P(params_entry) == IS_ARRAY || Z_TYPE_P(params_entry) == IS_OBJECT) { ++ ZVAL_DUP(entry, qarray_entry); ++ convert_to_array(entry); ++ php_http_querystring_update(entry, params_entry, NULL); ++ } else if ((FAILURE == is_equal_function(&equal, qarray_entry, params_entry)) || Z_TYPE(equal) != IS_TRUE) { ++ Z_TRY_ADDREF_P(params_entry); ++ entry = params_entry; + } + + if (entry) { - if (key.type == HASH_KEY_IS_STRING) { - zend_hash_update(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &entry, sizeof(zval *), NULL); ++ if (key.key) { ++ zend_hash_update(Z_ARRVAL_P(qarray), key.key, entry); + } else { - zend_hash_index_update(Z_ARRVAL_P(qarray), key.num, (void *) &entry, sizeof(zval *), NULL); ++ zend_hash_index_update(Z_ARRVAL_P(qarray), key.h, entry); + } + } + } else { - zval *entry; ++ zval entry, *entry_ptr = &entry; + /* + * add + */ - if (Z_TYPE_PP(params_entry) == IS_OBJECT) { - MAKE_STD_ZVAL(entry); - array_init(entry); - php_http_querystring_update(entry, *params_entry, NULL TSRMLS_CC); ++ if (Z_TYPE_P(params_entry) == IS_OBJECT) { ++ array_init(&entry); ++ php_http_querystring_update(&entry, params_entry, NULL); + } else { - Z_ADDREF_PP(params_entry); - entry = *params_entry; ++ Z_TRY_ADDREF_P(params_entry); ++ entry_ptr = params_entry; + } - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_zval_ex(qarray, key.str, key.len, entry); ++ if (key.key) { ++ add_assoc_zval_ex(qarray, key.key->val, key.key->len, entry_ptr); + } else { - add_index_zval(qarray, key.num, entry); ++ add_index_zval(qarray, key.h, entry_ptr); + } + } + } + } - /* clean up */ - if (zv_ptr) { - zval_ptr_dtor(&zv_ptr); - } ++ ZEND_HASH_FOREACH_END(); ++ + zval_dtor(&zv); + } + + /* serialize to string */ + if (outstring) { + char *s; + size_t l; + - if (SUCCESS == php_http_url_encode_hash(Z_ARRVAL_P(qarray), NULL, 0, &s, &l TSRMLS_CC)) { ++ if (SUCCESS == php_http_url_encode_hash(Z_ARRVAL_P(qarray), NULL, 0, &s, &l)) { + zval_dtor(outstring); - ZVAL_STRINGL(outstring, s, l, 0); ++ ZVAL_STR(outstring, php_http_cs2zs(s, l)); + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to encode query string"); ++ php_error_docref(NULL, E_WARNING, "Failed to encode query string"); + return FAILURE; + } + } + + return SUCCESS; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___construct, 0, 0, 0) + ZEND_ARG_INFO(0, params) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, __construct) + { + zval *params = NULL; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", ¶ms), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z", ¶ms), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC); - php_http_querystring_set(getThis(), params, 0 TSRMLS_CC); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh); ++ php_http_querystring_set(getThis(), params, 0); ++ zend_restore_error_handling(&zeh); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getGlobalInstance, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, getGlobalInstance) + { - zval *instance; ++ zval *instance, *_GET; ++ zend_string *zs; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - instance = *zend_std_get_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), 0 PHP_HTTP_ZEND_LITERAL_CCN TSRMLS_CC); - - if (Z_TYPE_P(instance) != IS_OBJECT) { - zval **_GET = NULL; - - zend_is_auto_global("_GET", lenof("_GET") TSRMLS_CC); ++ zs = zend_string_init(ZEND_STRL("instance"), 0); ++ instance = zend_std_get_static_property(php_http_querystring_class_entry, zs, 0); ++ zend_string_release(zs); + - if ((SUCCESS == zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (void *) &_GET)) - && (Z_TYPE_PP(_GET) == IS_ARRAY) - ) { - MAKE_STD_ZVAL(instance); - ZVAL_OBJVAL(instance, php_http_querystring_object_new(php_http_querystring_class_entry TSRMLS_CC), 0); ++ if (Z_TYPE_P(instance) == IS_OBJECT) { ++ RETVAL_ZVAL(instance, 1, 0); ++ } else if ((_GET = php_http_env_get_superglobal(ZEND_STRL("_GET")))) { ++ ZVAL_OBJ(return_value, php_http_querystring_object_new(php_http_querystring_class_entry)); + - SEPARATE_ZVAL_TO_MAKE_IS_REF(_GET); - convert_to_array(*_GET); - zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), *_GET TSRMLS_CC); ++ ZVAL_MAKE_REF(_GET); ++ zend_update_property(php_http_querystring_class_entry, return_value, ZEND_STRL("queryArray"), _GET); + - zend_update_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), instance TSRMLS_CC); - zval_ptr_dtor(&instance); - } else { - php_http_throw(unexpected_val, "Could not acquire reference to superglobal GET array", NULL); - } ++ zend_update_static_property(php_http_querystring_class_entry, ZEND_STRL("instance"), return_value); ++ } else { ++ php_http_throw(unexpected_val, "Could not acquire reference to superglobal GET array", NULL); + } + - RETVAL_ZVAL(instance, 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getIterator, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, getIterator) + { - zval *retval = NULL, *qa; ++ zval qa_tmp, *qa; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + - qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0, &qa_tmp); + + object_init_ex(return_value, spl_ce_RecursiveArrayIterator); - zend_call_method_with_1_params(&return_value, spl_ce_RecursiveArrayIterator, NULL, "__construct", &retval, qa); - if (retval) { - zval_ptr_dtor(&retval); - } ++ zend_call_method_with_1_params(return_value, spl_ce_RecursiveArrayIterator, NULL, "__construct", NULL, qa); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toString, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, toString) + { + if (SUCCESS != zend_parse_parameters_none()) { + return; + } - php_http_querystring_str(getThis(), return_value TSRMLS_CC); ++ php_http_querystring_str(getThis(), return_value); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toArray, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, toArray) + { - zval *zqa; ++ zval zqa_tmp, *zqa; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + - zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0, &zqa_tmp); + RETURN_ZVAL(zqa, 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_get, 0, 0, 0) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, defval) + ZEND_ARG_INFO(0, delete) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, get) + { + char *name_str = NULL; - int name_len = 0; - long type = 0; ++ size_t name_len = 0; ++ zend_long type = 0; + zend_bool del = 0; + zval *ztype = NULL, *defval = NULL; + - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|szzb", &name_str, &name_len, &ztype, &defval, &del)) { ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|szzb", &name_str, &name_len, &ztype, &defval, &del)) { + if (name_str && name_len) { + if (ztype) { + if (Z_TYPE_P(ztype) == IS_LONG) { + type = Z_LVAL_P(ztype); + } else if(Z_TYPE_P(ztype) == IS_STRING) { + switch (Z_STRVAL_P(ztype)[0]) { + case 'B': + case 'b': type = PHP_HTTP_QUERYSTRING_TYPE_BOOL; break; + case 'L': + case 'l': + case 'I': + case 'i': type = PHP_HTTP_QUERYSTRING_TYPE_INT; break; + case 'd': + case 'D': + case 'F': + case 'f': type = PHP_HTTP_QUERYSTRING_TYPE_FLOAT; break; + case 'S': + case 's': type = PHP_HTTP_QUERYSTRING_TYPE_STRING; break; + case 'A': + case 'a': type = PHP_HTTP_QUERYSTRING_TYPE_ARRAY; break; + case 'O': + case 'o': type = PHP_HTTP_QUERYSTRING_TYPE_OBJECT; break; + } + } + } - php_http_querystring_get(getThis(), type, name_str, name_len, defval, del, return_value TSRMLS_CC); ++ php_http_querystring_get(getThis(), type, name_str, name_len, defval, del, return_value); + } else { - php_http_querystring_str(getThis(), return_value TSRMLS_CC); ++ php_http_querystring_str(getThis(), return_value); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_set, 0, 0, 1) + ZEND_ARG_INFO(0, params) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, set) + { + zval *params; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶ms)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "z", ¶ms)) { + return; + } + - php_http_querystring_set(getThis(), params, QS_MERGE TSRMLS_CC); ++ php_http_querystring_set(getThis(), params, QS_MERGE); + RETVAL_ZVAL(getThis(), 1, 0); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_mod, 0, 0, 0) + ZEND_ARG_INFO(0, params) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, mod) + { - zval *params; ++ zval qa_tmp, *params, *instance = getThis(); + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶ms), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z", ¶ms), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh TSRMLS_CC); - ZVAL_OBJVAL(return_value, Z_OBJ_HT_P(getThis())->clone_obj(getThis() TSRMLS_CC), 0); - php_http_querystring_set(return_value, params, QS_MERGE TSRMLS_CC); - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_querystring_class_entry, &zeh); ++ ZVAL_OBJ(return_value, Z_OBJ_HT_P(instance)->clone_obj(instance)); ++ /* make sure we do not inherit the reference to _GET */ ++ SEPARATE_ZVAL(zend_read_property(Z_OBJCE_P(return_value), return_value, ZEND_STRL("queryArray"), 0, &qa_tmp)); ++ php_http_querystring_set(return_value, params, QS_MERGE); ++ zend_restore_error_handling(&zeh); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString___getter, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, defval) + ZEND_ARG_INFO(0, delete) + ZEND_END_ARG_INFO(); + #define PHP_HTTP_QUERYSTRING_GETTER(method, TYPE) \ + PHP_METHOD(HttpQueryString, method) \ + { \ + char *name; \ - int name_len; \ ++ size_t name_len; \ + zval *defval = NULL; \ + zend_bool del = 0; \ - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|zb", &name, &name_len, &defval, &del)) { \ - php_http_querystring_get(getThis(), TYPE, name, name_len, defval, del, return_value TSRMLS_CC); \ ++ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "s|zb", &name, &name_len, &defval, &del)) { \ ++ php_http_querystring_get(getThis(), TYPE, name, name_len, defval, del, return_value); \ + } \ + } -PHP_HTTP_QUERYSTRING_GETTER(getBool, IS_BOOL); ++PHP_HTTP_QUERYSTRING_GETTER(getBool, _IS_BOOL); + PHP_HTTP_QUERYSTRING_GETTER(getInt, IS_LONG); + PHP_HTTP_QUERYSTRING_GETTER(getFloat, IS_DOUBLE); + PHP_HTTP_QUERYSTRING_GETTER(getString, IS_STRING); + PHP_HTTP_QUERYSTRING_GETTER(getArray, IS_ARRAY); + PHP_HTTP_QUERYSTRING_GETTER(getObject, IS_OBJECT); + + #ifdef PHP_HTTP_HAVE_ICONV + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_xlate, 0, 0, 2) + ZEND_ARG_INFO(0, from_encoding) + ZEND_ARG_INFO(0, to_encoding) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, xlate) + { + char *ie, *oe; - int ie_len, oe_len; - zval *na, *qa; ++ size_t ie_len, oe_len; ++ zval na, qa_tmp, *qa; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ie, &ie_len, &oe, &oe_len), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &ie, &ie_len, &oe, &oe_len), invalid_arg, return); + - MAKE_STD_ZVAL(na); - array_init(na); - qa = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC)); ++ array_init(&na); ++ qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0, &qa_tmp); ++ ZVAL_DEREF(qa); ++ convert_to_array(qa); + - php_http_expect(SUCCESS == php_http_querystring_xlate(na, qa, ie, oe TSRMLS_CC), bad_conversion, ++ php_http_expect(SUCCESS == php_http_querystring_xlate(&na, qa, ie, oe), bad_conversion, + zval_ptr_dtor(&na); - zval_ptr_dtor(&qa); + return; + ); + - php_http_querystring_set(getThis(), na, 0 TSRMLS_CC); ++ php_http_querystring_set(getThis(), &na, 0); + RETVAL_ZVAL(getThis(), 1, 0); + + zval_ptr_dtor(&na); - zval_ptr_dtor(&qa); + } + #endif /* HAVE_ICONV */ + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_serialize, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, serialize) + { + if (SUCCESS != zend_parse_parameters_none()) { + return; + } - php_http_querystring_str(getThis(), return_value TSRMLS_CC); ++ php_http_querystring_str(getThis(), return_value); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_unserialize, 0, 0, 1) + ZEND_ARG_INFO(0, serialized) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, unserialize) + { + zval *serialized; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &serialized)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "z", &serialized)) { + return; + } + + if (Z_TYPE_P(serialized) == IS_STRING) { - php_http_querystring_set(getThis(), serialized, 0 TSRMLS_CC); ++ php_http_querystring_set(getThis(), serialized, 0); + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected a string as parameter"); ++ php_error_docref(NULL, E_WARNING, "Expected a string as parameter"); + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetGet, 0, 0, 1) + ZEND_ARG_INFO(0, offset) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, offsetGet) + { - char *offset_str; - int offset_len; - zval **value, *qa; ++ zend_string *offset; ++ zval *value, qa_tmp, *qa; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &offset)) { + return; + } + - qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0, &qa_tmp); ++ ZVAL_DEREF(qa); + + if (Z_TYPE_P(qa) == IS_ARRAY) { - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) { - RETVAL_ZVAL(*value, 1, 0); ++ if ((value = zend_symtable_find(Z_ARRVAL_P(qa), offset))) { ++ RETVAL_ZVAL(value, 1, 0); + } + } + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetSet, 0, 0, 2) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, value) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, offsetSet) + { - char *offset_str; - int offset_len; - zval *value, *param; ++ zend_string *offset; ++ zval *value, param, znull; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &value)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &offset, &value)) { + return; + } + - param = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); - - if (Z_TYPE_P(param) == IS_ARRAY && zend_symtable_exists(Z_ARRVAL_P(param), offset_str, offset_len + 1)) { - Z_ADDREF_P(value); - zend_symtable_update(Z_ARRVAL_P(param), offset_str, offset_len + 1, (void *) &value, sizeof(zval *), NULL); - Z_ADDREF_P(param); - } else { - MAKE_STD_ZVAL(param); - array_init(param); - Z_ADDREF_P(value); - add_assoc_zval_ex(param, offset_str, offset_len + 1, value); - } - php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC); ++ array_init_size(¶m, 1); ++ /* unset first */ ++ ZVAL_NULL(&znull); ++ zend_symtable_update(Z_ARRVAL(param), offset, &znull); ++ php_http_querystring_set(getThis(), ¶m, QS_MERGE); ++ /* then update, else QS_MERGE would merge sub-arrrays */ ++ Z_TRY_ADDREF_P(value); ++ zend_symtable_update(Z_ARRVAL(param), offset, value); ++ php_http_querystring_set(getThis(), ¶m, QS_MERGE); + zval_ptr_dtor(¶m); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetExists, 0, 0, 1) + ZEND_ARG_INFO(0, offset) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, offsetExists) + { - char *offset_str; - int offset_len; - zval **value, *qa; ++ zend_string *offset; ++ zval *value, qa_tmp, *qa; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &offset)) { + return; + } + - qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); ++ qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0, &qa_tmp); ++ ZVAL_DEREF(qa); + + if (Z_TYPE_P(qa) == IS_ARRAY) { - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(qa), offset_str, offset_len + 1, (void *) &value)) { - RETURN_BOOL(Z_TYPE_PP(value) != IS_NULL); ++ if ((value = zend_symtable_find(Z_ARRVAL_P(qa), offset))) { ++ RETURN_BOOL(Z_TYPE_P(value) != IS_NULL); + } + } + RETURN_FALSE; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_offsetUnset, 0, 0, 1) + ZEND_ARG_INFO(0, offset) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpQueryString, offsetUnset) + { - char *offset_str; - int offset_len; - zval *param; ++ zend_string *offset; ++ zval param, znull; + - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { ++ if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "S", &offset)) { + return; + } + - MAKE_STD_ZVAL(param); - array_init(param); - add_assoc_null_ex(param, offset_str, offset_len + 1); - php_http_querystring_set(getThis(), param, QS_MERGE TSRMLS_CC); ++ array_init(¶m); ++ ZVAL_NULL(&znull); ++ zend_symtable_update(Z_ARRVAL(param), offset, &znull); ++ php_http_querystring_set(getThis(), ¶m, QS_MERGE); + zval_ptr_dtor(¶m); + } + + zend_class_entry *php_http_querystring_class_entry; + + static zend_function_entry php_http_querystring_methods[] = { + PHP_ME(HttpQueryString, __construct, ai_HttpQueryString___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR|ZEND_ACC_FINAL) + + PHP_ME(HttpQueryString, toArray, ai_HttpQueryString_toArray, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpQueryString, __toString, toString, ai_HttpQueryString_toString, ZEND_ACC_PUBLIC) + + PHP_ME(HttpQueryString, get, ai_HttpQueryString_get, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, set, ai_HttpQueryString_set, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, mod, ai_HttpQueryString_mod, ZEND_ACC_PUBLIC) + + PHP_ME(HttpQueryString, getBool, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, getInt, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, getFloat, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, getString, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, getArray, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, getObject, ai_HttpQueryString___getter, ZEND_ACC_PUBLIC) + + PHP_ME(HttpQueryString, getIterator, ai_HttpQueryString_getIterator, ZEND_ACC_PUBLIC) + + PHP_ME(HttpQueryString, getGlobalInstance, ai_HttpQueryString_getGlobalInstance, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + #ifdef PHP_HTTP_HAVE_ICONV + PHP_ME(HttpQueryString, xlate, ai_HttpQueryString_xlate, ZEND_ACC_PUBLIC) + #endif + + /* Implements Serializable */ + PHP_ME(HttpQueryString, serialize, ai_HttpQueryString_serialize, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, unserialize, ai_HttpQueryString_unserialize, ZEND_ACC_PUBLIC) + + /* Implements ArrayAccess */ + PHP_ME(HttpQueryString, offsetGet, ai_HttpQueryString_offsetGet, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, offsetSet, ai_HttpQueryString_offsetSet, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, offsetExists, ai_HttpQueryString_offsetExists, ZEND_ACC_PUBLIC) + PHP_ME(HttpQueryString, offsetUnset, ai_HttpQueryString_offsetUnset, ZEND_ACC_PUBLIC) + + EMPTY_FUNCTION_ENTRY + }; + + PHP_MINIT_FUNCTION(http_querystring) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "QueryString", php_http_querystring_methods); - php_http_querystring_class_entry = zend_register_internal_class(&ce TSRMLS_CC); ++ php_http_querystring_class_entry = zend_register_internal_class(&ce); + php_http_querystring_class_entry->create_object = php_http_querystring_object_new; - zend_class_implements(php_http_querystring_class_entry TSRMLS_CC, 3, zend_ce_serializable, zend_ce_arrayaccess, zend_ce_aggregate); ++ zend_class_implements(php_http_querystring_class_entry, 3, zend_ce_serializable, zend_ce_arrayaccess, zend_ce_aggregate); + - zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("instance"), (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE) TSRMLS_CC); - zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("queryArray"), ZEND_ACC_PRIVATE TSRMLS_CC); ++ zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("instance"), (ZEND_ACC_STATIC|ZEND_ACC_PRIVATE)); ++ zend_declare_property_null(php_http_querystring_class_entry, ZEND_STRL("queryArray"), ZEND_ACC_PRIVATE); + - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_BOOL"), PHP_HTTP_QUERYSTRING_TYPE_BOOL TSRMLS_CC); - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_INT"), PHP_HTTP_QUERYSTRING_TYPE_INT TSRMLS_CC); - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_FLOAT"), PHP_HTTP_QUERYSTRING_TYPE_FLOAT TSRMLS_CC); - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_STRING"), PHP_HTTP_QUERYSTRING_TYPE_STRING TSRMLS_CC); - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_ARRAY"), PHP_HTTP_QUERYSTRING_TYPE_ARRAY TSRMLS_CC); - zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_OBJECT"), PHP_HTTP_QUERYSTRING_TYPE_OBJECT TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_BOOL"), PHP_HTTP_QUERYSTRING_TYPE_BOOL); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_INT"), PHP_HTTP_QUERYSTRING_TYPE_INT); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_FLOAT"), PHP_HTTP_QUERYSTRING_TYPE_FLOAT); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_STRING"), PHP_HTTP_QUERYSTRING_TYPE_STRING); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_ARRAY"), PHP_HTTP_QUERYSTRING_TYPE_ARRAY); ++ zend_declare_class_constant_long(php_http_querystring_class_entry, ZEND_STRL("TYPE_OBJECT"), PHP_HTTP_QUERYSTRING_TYPE_OBJECT); + + return SUCCESS; + } + + /* + * 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 --cc src/php_http_querystring.h index 0000000,3391e91..d41b8e9 mode 000000,100644..100644 --- a/src/php_http_querystring.h +++ b/src/php_http_querystring.h @@@ -1,0 -1,47 +1,47 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_QUERYSTRING_H + #define PHP_HTTP_QUERYSTRING_H + + #ifdef PHP_HTTP_HAVE_ICONV -PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe TSRMLS_DC); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_xlate(zval *dst, zval *src, const char *ie, const char *oe); + #endif /* PHP_HTTP_HAVE_ICONV */ -PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *qstring TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_update(zval *qarray, zval *params, zval *qstring); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params); + + typedef php_http_object_t php_http_querystring_object_t; + -#define PHP_HTTP_QUERYSTRING_TYPE_BOOL IS_BOOL ++#define PHP_HTTP_QUERYSTRING_TYPE_BOOL _IS_BOOL + #define PHP_HTTP_QUERYSTRING_TYPE_INT IS_LONG + #define PHP_HTTP_QUERYSTRING_TYPE_FLOAT IS_DOUBLE + #define PHP_HTTP_QUERYSTRING_TYPE_STRING IS_STRING + #define PHP_HTTP_QUERYSTRING_TYPE_ARRAY IS_ARRAY + #define PHP_HTTP_QUERYSTRING_TYPE_OBJECT IS_OBJECT + + PHP_HTTP_API zend_class_entry *php_http_querystring_class_entry; + + PHP_MINIT_FUNCTION(http_querystring); + + #define php_http_querystring_object_new php_http_object_new + #define php_http_querystring_object_new_ex php_http_object_new_ex + + #endif /* PHP_HTTP_QUERYSTRING_H */ + + /* + * 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 --cc src/php_http_url.c index 0000000,81b2d95..c5c19a5 mode 000000,100644..100644 --- a/src/php_http_url.c +++ b/src/php_http_url.c @@@ -1,0 -1,1749 +1,1748 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + + #if PHP_HTTP_HAVE_IDN2 + # include + #elif PHP_HTTP_HAVE_IDN + # include + #endif + + #ifdef PHP_HTTP_HAVE_WCHAR + # include + # include + #endif + + #ifdef HAVE_ARPA_INET_H + # include + #endif + + #include "php_http_utf8.h" + + static inline char *localhostname(void) + { + char hostname[1024] = {0}; + + #ifdef PHP_WIN32 + if (SUCCESS == gethostname(hostname, lenof(hostname))) { + return estrdup(hostname); + } + #elif defined(HAVE_GETHOSTNAME) + if (SUCCESS == gethostname(hostname, lenof(hostname))) { + # if defined(HAVE_GETDOMAINNAME) + size_t hlen = strlen(hostname); + if (hlen <= lenof(hostname) - lenof("(none)")) { + hostname[hlen++] = '.'; + if (SUCCESS == getdomainname(&hostname[hlen], lenof(hostname) - hlen)) { + if (!strcmp(&hostname[hlen], "(none)")) { + hostname[hlen - 1] = '\0'; + } + return estrdup(hostname); + } + } + # endif + if (strcmp(hostname, "(none)")) { + return estrdup(hostname); + } + } + #endif + return estrndup("localhost", lenof("localhost")); + } + + #define url(buf) ((php_http_url_t *) (buf).data) + -static php_http_url_t *php_http_url_from_env(TSRMLS_D) ++static php_http_url_t *php_http_url_from_env(void) + { + zval *https, *zhost, *zport; + long port; + php_http_buffer_t buf; + + php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC); + php_http_buffer_account(&buf, sizeof(php_http_url_t)); + memset(buf.data, 0, buf.used); + + /* scheme */ + url(buf)->scheme = &buf.data[buf.used]; - https = php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC); ++ https = php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1); + if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) { + php_http_buffer_append(&buf, "https", sizeof("https")); + } else { + php_http_buffer_append(&buf, "http", sizeof("http")); + } + + /* host */ + url(buf)->host = &buf.data[buf.used]; - if ((((zhost = php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC)) || - (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC)) || - (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC)))) && Z_STRLEN_P(zhost)) { ++ if ((((zhost = php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1)) || ++ (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1)) || ++ (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1)))) && Z_STRLEN_P(zhost)) { + size_t stop_at = strspn(Z_STRVAL_P(zhost), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-."); + + php_http_buffer_append(&buf, Z_STRVAL_P(zhost), stop_at); + php_http_buffer_append(&buf, "", 1); + } else { + char *host_str = localhostname(); + + php_http_buffer_append(&buf, host_str, strlen(host_str) + 1); + efree(host_str); + } + + /* port */ - zport = php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC); ++ zport = php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1); + if (zport && IS_LONG == is_numeric_string(Z_STRVAL_P(zport), Z_STRLEN_P(zport), &port, NULL, 0)) { + url(buf)->port = port; + } + + /* path */ + if (SG(request_info).request_uri && SG(request_info).request_uri[0]) { + const char *q = strchr(SG(request_info).request_uri, '?'); + + url(buf)->path = &buf.data[buf.used]; + + if (q) { + php_http_buffer_append(&buf, SG(request_info).request_uri, q - SG(request_info).request_uri); + php_http_buffer_append(&buf, "", 1); + } else { + php_http_buffer_append(&buf, SG(request_info).request_uri, strlen(SG(request_info).request_uri) + 1); + } + } + + /* query */ + if (SG(request_info).query_string && SG(request_info).query_string[0]) { + url(buf)->query = &buf.data[buf.used]; + php_http_buffer_append(&buf, SG(request_info).query_string, strlen(SG(request_info).query_string) + 1); + } + + return url(buf); + } + + #define url_isset(u,n) \ + ((u)&&(u)->n) + #define url_append(buf, append) do { \ + char *_ptr = (buf)->data; \ + php_http_url_t *_url = (php_http_url_t *) _ptr, _mem = *_url; \ + append; \ + /* relocate */ \ + if (_ptr != (buf)->data) { \ + ptrdiff_t diff = (buf)->data - _ptr; \ + _url = (php_http_url_t *) (buf)->data; \ + if (_mem.scheme) _url->scheme += diff; \ + if (_mem.user) _url->user += diff; \ + if (_mem.pass) _url->pass += diff; \ + if (_mem.host) _url->host += diff; \ + if (_mem.path) _url->path += diff; \ + if (_mem.query) _url->query += diff; \ + if (_mem.fragment) _url->fragment += diff; \ + } \ + } while (0) + #define url_copy(n) do { \ + if (url_isset(new_url, n)) { \ + url(buf)->n = &buf.data[buf.used]; \ + url_append(&buf, php_http_buffer_append(&buf, new_url->n, strlen(new_url->n) + 1)); \ + } else if (url_isset(old_url, n)) { \ + url(buf)->n = &buf.data[buf.used]; \ + url_append(&buf, php_http_buffer_append(&buf, old_url->n, strlen(old_url->n) + 1)); \ + } \ + } while (0) + -php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC) ++php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags) + { + php_http_url_t *tmp_url = NULL; + php_http_buffer_t buf; + + php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC); + php_http_buffer_account(&buf, sizeof(php_http_url_t)); + memset(buf.data, 0, buf.used); + + /* set from env if requested */ + if (flags & PHP_HTTP_URL_FROM_ENV) { - php_http_url_t *env_url = php_http_url_from_env(TSRMLS_C); ++ php_http_url_t *env_url = php_http_url_from_env(); + - old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV TSRMLS_CC); ++ old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV); + php_http_url_free(&env_url); + } + + url_copy(scheme); + + if (!(flags & PHP_HTTP_URL_STRIP_USER)) { + url_copy(user); + } + + if (!(flags & PHP_HTTP_URL_STRIP_PASS)) { + url_copy(pass); + } + + url_copy(host); + + if (!(flags & PHP_HTTP_URL_STRIP_PORT)) { + url(buf)->port = url_isset(new_url, port) ? new_url->port : ((old_url) ? old_url->port : 0); + } + + if (!(flags & PHP_HTTP_URL_STRIP_PATH)) { + if ((flags & PHP_HTTP_URL_JOIN_PATH) && url_isset(old_url, path) && url_isset(new_url, path) && *new_url->path != '/') { + size_t old_path_len = strlen(old_url->path), new_path_len = strlen(new_url->path); + char *path = ecalloc(1, old_path_len + new_path_len + 1 + 1); + + strcat(path, old_url->path); + if (path[old_path_len - 1] != '/') { + php_dirname(path, old_path_len); + strcat(path, "/"); + } + strcat(path, new_url->path); + + url(buf)->path = &buf.data[buf.used]; + if (path[0] != '/') { + url_append(&buf, php_http_buffer_append(&buf, "/", 1)); + } + url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1)); + efree(path); + } else { + const char *path = NULL; + + if (url_isset(new_url, path)) { + path = new_url->path; + } else if (url_isset(old_url, path)) { + path = old_url->path; + } + + if (path) { + url(buf)->path = &buf.data[buf.used]; + + url_append(&buf, php_http_buffer_append(&buf, path, strlen(path) + 1)); + } + + + } + } + + if (!(flags & PHP_HTTP_URL_STRIP_QUERY)) { + if ((flags & PHP_HTTP_URL_JOIN_QUERY) && url_isset(new_url, query) && url_isset(old_url, query)) { + zval qarr, qstr; + - INIT_PZVAL(&qstr); - INIT_PZVAL(&qarr); + array_init(&qarr); + - ZVAL_STRING(&qstr, old_url->query, 0); - php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC); - ZVAL_STRING(&qstr, new_url->query, 0); - php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC); ++ ZVAL_STRING(&qstr, old_url->query); ++ php_http_querystring_update(&qarr, &qstr, NULL); ++ zval_ptr_dtor(&qstr); ++ ZVAL_STRING(&qstr, new_url->query); ++ php_http_querystring_update(&qarr, &qstr, NULL); ++ zval_ptr_dtor(&qstr); + + ZVAL_NULL(&qstr); - php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC); ++ php_http_querystring_update(&qarr, NULL, &qstr); + + url(buf)->query = &buf.data[buf.used]; + url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL(qstr), Z_STRLEN(qstr) + 1)); + + zval_dtor(&qstr); + zval_dtor(&qarr); + } else { + url_copy(query); + } + } + + if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) { + url_copy(fragment); + } + + /* done with copy & combine & strip */ + + if (flags & PHP_HTTP_URL_FROM_ENV) { + /* free old_url we tainted above */ + php_http_url_free(&tmp_url); + } + + /* replace directory references if path is not a single slash */ + if ((flags & PHP_HTTP_URL_SANITIZE_PATH) + && url(buf)->path[0] && url(buf)->path[1]) { + char *ptr, *end = url(buf)->path + strlen(url(buf)->path) + 1; + + for (ptr = strchr(url(buf)->path, '/'); ptr; ptr = strchr(ptr, '/')) { + switch (ptr[1]) { + case '/': + memmove(&ptr[1], &ptr[2], end - &ptr[2]); + break; + + case '.': + switch (ptr[2]) { + case '\0': + ptr[1] = '\0'; + break; + + case '/': + memmove(&ptr[1], &ptr[3], end - &ptr[3]); + break; + + case '.': + if (ptr[3] == '/') { + char *pos = &ptr[4]; + while (ptr != url(buf)->path) { + if (*--ptr == '/') { + break; + } + } + memmove(&ptr[1], pos, end - pos); + break; + } else if (!ptr[3]) { + /* .. at the end */ + ptr[1] = '\0'; + } + /* no break */ + + default: + /* something else */ + ++ptr; + break; + } + break; + + default: + ++ptr; + break; + } + } + } + /* unset default ports */ + if (url(buf)->port) { + if ( ((url(buf)->port == 80) && url(buf)->scheme && !strcmp(url(buf)->scheme, "http")) + || ((url(buf)->port ==443) && url(buf)->scheme && !strcmp(url(buf)->scheme, "https")) + ) { + url(buf)->port = 0; + } + } + + return url(buf); + } + + char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent) + { + php_http_buffer_t buf; + + php_http_buffer_init_ex(&buf, PHP_HTTP_BUFFER_DEFAULT_SIZE, persistent ? + PHP_HTTP_BUFFER_INIT_PERSISTENT : 0); + + if (url->scheme && *url->scheme) { + php_http_buffer_appendl(&buf, url->scheme); + php_http_buffer_appends(&buf, "://"); + } else if ((url->user && *url->user) || (url->host && *url->host)) { + php_http_buffer_appends(&buf, "//"); + } + + if (url->user && *url->user) { + php_http_buffer_appendl(&buf, url->user); + if (url->pass && *url->pass) { + php_http_buffer_appends(&buf, ":"); + php_http_buffer_appendl(&buf, url->pass); + } + php_http_buffer_appends(&buf, "@"); + } + + if (url->host && *url->host) { + php_http_buffer_appendl(&buf, url->host); + if (url->port) { + php_http_buffer_appendf(&buf, ":%hu", url->port); + } + } + + if (url->path && *url->path) { + if (*url->path != '/') { + php_http_buffer_appends(&buf, "/"); + } + php_http_buffer_appendl(&buf, url->path); + } else if (buf.used) { + php_http_buffer_appends(&buf, "/"); + } + + if (url->query && *url->query) { + php_http_buffer_appends(&buf, "?"); + php_http_buffer_appendl(&buf, url->query); + } + + if (url->fragment && *url->fragment) { + php_http_buffer_appends(&buf, "#"); + php_http_buffer_appendl(&buf, url->fragment); + } + + php_http_buffer_shrink(&buf); + php_http_buffer_fix(&buf); + + if (url_len) { + *url_len = buf.used; + } + + if (url_str) { + *url_str = buf.data; + } + + return buf.data; + } + + char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len) + { + php_http_buffer_t buf; + + php_http_buffer_init(&buf); + + if (url->user && *url->user) { + php_http_buffer_appendl(&buf, url->user); + if (url->pass && *url->pass) { + php_http_buffer_appends(&buf, ":"); + php_http_buffer_appendl(&buf, url->pass); + } + php_http_buffer_appends(&buf, "@"); + } + + if (url->host && *url->host) { + php_http_buffer_appendl(&buf, url->host); + if (url->port) { + php_http_buffer_appendf(&buf, ":%hu", url->port); + } + } + + php_http_buffer_shrink(&buf); + php_http_buffer_fix(&buf); + + if (url_len) { + *url_len = buf.used; + } + + if (url_str) { + *url_str = buf.data; + } + + return buf.data; + } + -php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC) ++php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags) + { - zval *zcpy; ++ zend_string *zs; + php_http_url_t *purl; + + switch (Z_TYPE_P(value)) { + case IS_ARRAY: + case IS_OBJECT: + purl = php_http_url_from_struct(HASH_OF(value)); + break; + + default: - zcpy = php_http_ztyp(IS_STRING, value); - purl = php_http_url_parse(Z_STRVAL_P(zcpy), Z_STRLEN_P(zcpy), flags TSRMLS_CC); - zval_ptr_dtor(&zcpy); ++ zs = zval_get_string(value); ++ purl = php_http_url_parse(zs->val, zs->len, flags); ++ zend_string_release(zs); + } + + return purl; + } + + php_http_url_t *php_http_url_from_struct(HashTable *ht) + { - zval **e; ++ zval *e; + php_http_buffer_t buf; + + php_http_buffer_init_ex(&buf, MAX(PHP_HTTP_BUFFER_DEFAULT_SIZE, sizeof(php_http_url_t)<<2), PHP_HTTP_BUFFER_INIT_PREALLOC); + php_http_buffer_account(&buf, sizeof(php_http_url_t)); + memset(buf.data, 0, buf.used); + - if (SUCCESS == zend_hash_find(ht, "scheme", sizeof("scheme"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("scheme")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->scheme = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("user")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->user = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("pass")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->pass = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("host")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->host = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "port", sizeof("port"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_LONG, *e); - url(buf)->port = (unsigned short) Z_LVAL_P(cpy); - zval_ptr_dtor(&cpy); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("port")))) { ++ url(buf)->port = (unsigned short) zval_get_long(e); + } - if (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("path")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->path = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("query")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->query = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } - if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_STRING, *e); ++ if ((e = zend_hash_str_find_ind(ht, ZEND_STRL("fragment")))) { ++ zend_string *zs = zval_get_string(e); + url(buf)->fragment = &buf.data[buf.used]; - url_append(&buf, php_http_buffer_append(&buf, Z_STRVAL_P(cpy), Z_STRLEN_P(cpy) + 1)); - zval_ptr_dtor(&cpy); ++ url_append(&buf, php_http_buffer_append(&buf, zs->val, zs->len + 1)); ++ zend_string_release(zs); + } + + return url(buf); + } + -HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC) ++HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct) + { - zval arr; ++ HashTable *ht; ++ zval tmp; + + if (strct) { + switch (Z_TYPE_P(strct)) { + default: + zval_dtor(strct); + array_init(strct); + /* no break */ + case IS_ARRAY: + case IS_OBJECT: - INIT_PZVAL_ARRAY((&arr), HASH_OF(strct)); ++ ht = HASH_OF(strct); + break; + } + } else { - INIT_PZVAL(&arr); - array_init(&arr); ++ ALLOC_HASHTABLE(ht); ++ zend_hash_init(ht, 8, NULL, ZVAL_PTR_DTOR, 0); ++ } ++ ++#define url_struct_add(part) \ ++ if (Z_TYPE_P(strct) == IS_ARRAY) { \ ++ zend_hash_str_update(Z_ARRVAL_P(strct), part, lenof(part), &tmp); \ ++ } else { \ ++ zend_update_property(Z_OBJCE_P(strct), strct, part, lenof(part), &tmp); \ ++ zval_ptr_dtor(&tmp); \ + } + + if (url) { + if (url->scheme) { - add_assoc_string(&arr, "scheme", url->scheme, 1); ++ ZVAL_STRING(&tmp, url->scheme); ++ url_struct_add("scheme"); + } + if (url->user) { - add_assoc_string(&arr, "user", url->user, 1); ++ ZVAL_STRING(&tmp, url->user); ++ url_struct_add("user"); + } + if (url->pass) { - add_assoc_string(&arr, "pass", url->pass, 1); ++ ZVAL_STRING(&tmp, url->pass); ++ url_struct_add("pass"); + } + if (url->host) { - add_assoc_string(&arr, "host", url->host, 1); ++ ZVAL_STRING(&tmp, url->host); ++ url_struct_add("host"); + } + if (url->port) { - add_assoc_long(&arr, "port", (long) url->port); ++ ZVAL_LONG(&tmp, url->port); ++ url_struct_add("port"); + } + if (url->path) { - add_assoc_string(&arr, "path", url->path, 1); ++ ZVAL_STRING(&tmp, url->path); ++ url_struct_add("path"); + } + if (url->query) { - add_assoc_string(&arr, "query", url->query, 1); ++ ZVAL_STRING(&tmp, url->query); ++ url_struct_add("query"); + } + if (url->fragment) { - add_assoc_string(&arr, "fragment", url->fragment, 1); ++ ZVAL_STRING(&tmp, url->fragment); ++ url_struct_add("fragment"); + } + } + - return Z_ARRVAL(arr); ++ return ht; + } + -ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC) ++ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len) + { + const char *arg_sep_str = "&"; + size_t arg_sep_len = 1; + php_http_buffer_t *qstr = php_http_buffer_new(); + - php_http_url_argsep(&arg_sep_str, &arg_sep_len TSRMLS_CC); ++ php_http_url_argsep(&arg_sep_str, &arg_sep_len); + - if (SUCCESS != php_http_url_encode_hash_ex(hash, qstr, arg_sep_str, arg_sep_len, "=", 1, pre_encoded_str, pre_encoded_len TSRMLS_CC)) { ++ if (SUCCESS != php_http_url_encode_hash_ex(hash, qstr, arg_sep_str, arg_sep_len, "=", 1, pre_encoded_str, pre_encoded_len)) { + php_http_buffer_free(&qstr); + return FAILURE; + } + + php_http_buffer_data(qstr, encoded_str, encoded_len); + php_http_buffer_free(&qstr); + + return SUCCESS; + } + -ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC) ++ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len) + { + if (pre_encoded_len && pre_encoded_str) { + php_http_buffer_append(qstr, pre_encoded_str, pre_encoded_len); + } + - if (!php_http_params_to_string(qstr, hash, arg_sep_str, arg_sep_len, "", 0, val_sep_str, val_sep_len, PHP_HTTP_PARAMS_QUERY TSRMLS_CC)) { ++ if (!php_http_params_to_string(qstr, hash, arg_sep_str, arg_sep_len, "", 0, val_sep_str, val_sep_len, PHP_HTTP_PARAMS_QUERY)) { + return FAILURE; + } + + return SUCCESS; + } + + struct parse_state { + php_http_url_t url; -#ifdef ZTS - void ***ts; -#endif + const char *ptr; + const char *end; + size_t maxlen; + off_t offset; + unsigned flags; + char buffer[1]; /* last member */ + }; + + void php_http_url_free(php_http_url_t **url) + { + if (*url) { + efree(*url); + *url = NULL; + } + } + + php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent) + { + php_http_url_t *cpy; + const char *end = NULL, *url_ptr = (const char *) url; + char *cpy_ptr; + + end = MAX(url->scheme, end); + end = MAX(url->pass, end); + end = MAX(url->user, end); + end = MAX(url->host, end); + end = MAX(url->path, end); + end = MAX(url->query, end); + end = MAX(url->fragment, end); + + if (end) { + end += strlen(end) + 1; + cpy_ptr = pecalloc(1, end - url_ptr, persistent); + cpy = (php_http_url_t *) cpy_ptr; + + memcpy(cpy_ptr + sizeof(*cpy), url_ptr + sizeof(*url), end - url_ptr - sizeof(*url)); + + cpy->scheme = url->scheme ? cpy_ptr + (url->scheme - url_ptr) : NULL; + cpy->pass = url->pass ? cpy_ptr + (url->pass - url_ptr) : NULL; + cpy->user = url->user ? cpy_ptr + (url->user - url_ptr) : NULL; + cpy->host = url->host ? cpy_ptr + (url->host - url_ptr) : NULL; + cpy->path = url->path ? cpy_ptr + (url->path - url_ptr) : NULL; + cpy->query = url->query ? cpy_ptr + (url->query - url_ptr) : NULL; + cpy->fragment = url->fragment ? cpy_ptr + (url->fragment - url_ptr) : NULL; + } else { + cpy = ecalloc(1, sizeof(*url)); + } + + cpy->port = url->port; + + return cpy; + } + + static size_t parse_mb_utf8(unsigned *wc, const char *ptr, const char *end) + { + unsigned wchar; + size_t consumed = utf8towc(&wchar, (const unsigned char *) ptr, end - ptr); + + if (!consumed || consumed == (size_t) -1) { + return 0; + } + + if (wc) { + *wc = wchar; + } + return consumed; + } + + #ifdef PHP_HTTP_HAVE_WCHAR + static size_t parse_mb_loc(unsigned *wc, const char *ptr, const char *end) + { + wchar_t wchar; + size_t consumed = 0; + #if defined(HAVE_MBRTOWC) + mbstate_t ps; + + memset(&ps, 0, sizeof(ps)); + consumed = mbrtowc(&wchar, ptr, end - ptr, &ps); + #elif defined(HAVE_MBTOWC) + consumed = mbtowc(&wchar, ptr, end - ptr); + #endif + + if (!consumed || consumed == (size_t) -1) { + return 0; + } + + if (wc) { + *wc = wchar; + } + return consumed; + } + #endif + + typedef enum parse_mb_what { + PARSE_SCHEME, + PARSE_USERINFO, + PARSE_HOSTINFO, + PARSE_PATH, + PARSE_QUERY, + PARSE_FRAGMENT + } parse_mb_what_t; + + static const char * const parse_what[] = { + "scheme", + "userinfo", + "hostinfo", + "path", + "query", + "fragment" + }; + + static const char parse_xdigits[] = "0123456789ABCDEF"; + + static size_t parse_mb(struct parse_state *state, parse_mb_what_t what, const char *ptr, const char *end, const char *begin, zend_bool silent) + { + unsigned wchar; + size_t consumed = 0; + + if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { + consumed = parse_mb_utf8(&wchar, ptr, end); + } + #ifdef PHP_HTTP_HAVE_WCHAR + else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { + consumed = parse_mb_loc(&wchar, ptr, end); + } + #endif + + while (consumed) { + if (!(state->flags & PHP_HTTP_URL_PARSE_TOPCT) || what == PARSE_HOSTINFO || what == PARSE_SCHEME) { + if (what == PARSE_HOSTINFO && (state->flags & PHP_HTTP_URL_PARSE_TOIDN)) { + /* idna */ + } else if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { + if (!isualnum(wchar)) { + break; + } + #ifdef PHP_HTTP_HAVE_WCHAR + } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { + if (!iswalnum(wchar)) { + break; + } + #endif + } + PHP_HTTP_DUFF(consumed, state->buffer[state->offset++] = *ptr++); + } else { + int i = 0; + + PHP_HTTP_DUFF(consumed, + state->buffer[state->offset++] = '%'; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) >> 4]; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) ptr[i]) & 0xf]; + ++i; + ); + } + + return consumed; + } + + if (!silent) { - TSRMLS_FETCH_FROM_CTX(state->ts); + if (consumed) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse %s; unexpected multibyte sequence 0x%x at pos %u in '%s'", + parse_what[what], wchar, (unsigned) (ptr - begin), begin); + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'", + parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin); + } + } + + return 0; + } + + static ZEND_RESULT_CODE parse_userinfo(struct parse_state *state, const char *ptr) + { + size_t mb; + const char *password = NULL, *end = state->ptr, *tmp = ptr; - TSRMLS_FETCH_FROM_CTX(state->ts); + + state->url.user = &state->buffer[state->offset]; + + do { + switch (*ptr) { + case ':': + if (password) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse password; duplicate ':' at pos %u in '%s'", + (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + password = ptr + 1; + state->buffer[state->offset++] = 0; + state->url.pass = &state->buffer[state->offset]; + break; + + case '%': + if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'", + (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + state->buffer[state->offset++] = *ptr++; + state->buffer[state->offset++] = *ptr++; + state->buffer[state->offset++] = *ptr; + break; + + case '!': case '$': case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case ';': case '=': /* sub-delims */ + case '-': case '.': case '_': case '~': /* unreserved */ + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + /* allowed */ + state->buffer[state->offset++] = *ptr; + break; + + default: + if (!(mb = parse_mb(state, PARSE_USERINFO, ptr, end, tmp, 0))) { + return FAILURE; + } + ptr += mb - 1; + } + } while(++ptr != end); + + + state->buffer[state->offset++] = 0; + + return SUCCESS; + } + + #if defined(PHP_WIN32) || defined(HAVE_UIDNA_IDNTOASCII) + typedef size_t (*parse_mb_func)(unsigned *wc, const char *ptr, const char *end); -static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len TSRMLS_DC) ++static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u16, size_t *len) + { + size_t offset = 0, u8_len = strlen(u8); + + *u16 = ecalloc(4 * sizeof(uint16_t), u8_len + 1); + *len = 0; + + while (offset < u8_len) { + unsigned wc; + uint16_t buf[2], *ptr = buf; + size_t consumed = fn(&wc, &u8[offset], &u8[u8_len]); + + if (!consumed) { + efree(*u16); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse UTF-8 at pos %zu of '%s'", offset, u8); ++ php_error_docref(NULL, E_WARNING, "Failed to parse UTF-8 at pos %zu of '%s'", offset, u8); + return FAILURE; + } else { + offset += consumed; + } + + switch (wctoutf16(buf, wc)) { + case 2: + (*u16)[(*len)++] = *ptr++; + /* no break */ + case 1: + (*u16)[(*len)++] = *ptr++; + break; + case 0: + default: + efree(*u16); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to convert UTF-32 'U+%X' to UTF-16", wc); ++ php_error_docref(NULL, E_WARNING, "Failed to convert UTF-32 'U+%X' to UTF-16", wc); + return FAILURE; + } + } + + return SUCCESS; + } + #endif + + #ifndef MAXHOSTNAMELEN + # define MAXHOSTNAMELEN 256 + #endif + + #if PHP_HTTP_HAVE_IDN2 + static ZEND_RESULT_CODE parse_idn2(struct parse_state *state, size_t prev_len) + { + char *idn = NULL; + int rv = -1; - TSRMLS_FETCH_FROM_CTX(state->ts); + + if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { + rv = idn2_lookup_u8((const unsigned char *) state->url.host, (unsigned char **) &idn, IDN2_NFC_INPUT); + } + # ifdef PHP_HTTP_HAVE_WCHAR + else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { + rv = idn2_lookup_ul(state->url.host, &idn, 0); + } + # endif + if (rv != IDN2_OK) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idn2_strerror(rv)); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN; %s", idn2_strerror(rv)); + return FAILURE; + } else { + size_t idnlen = strlen(idn); + memcpy(state->url.host, idn, idnlen + 1); + free(idn); + state->offset += idnlen - prev_len; + return SUCCESS; + } + } + #elif PHP_HTTP_HAVE_IDN + static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len) + { + char *idn = NULL; + int rv = -1; - TSRMLS_FETCH_FROM_CTX(state->ts); + + if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { + rv = idna_to_ascii_8z(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES); + } + # ifdef PHP_HTTP_HAVE_WCHAR + else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { + rv = idna_to_ascii_lz(state->url.host, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES); + } + # endif + if (rv != IDNA_SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv)); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv)); + return FAILURE; + } else { + size_t idnlen = strlen(idn); + memcpy(state->url.host, idn, idnlen + 1); + free(idn); + state->offset += idnlen - prev_len; + return SUCCESS; + } + } + #endif + + #ifdef HAVE_UIDNA_IDNTOASCII + # if HAVE_UNICODE_UIDNA_H + # include + # else + typedef uint16_t UChar; + typedef enum { U_ZERO_ERROR = 0 } UErrorCode; + int32_t uidna_IDNToASCII(const UChar *src, int32_t srcLength, UChar *dest, int32_t destCapacity, int32_t options, void *parseError, UErrorCode *status); + # endif + static ZEND_RESULT_CODE parse_uidn(struct parse_state *state) + { + char *host_ptr; + uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr; + size_t uhost_len, ahost_len; + UErrorCode error = U_ZERO_ERROR; - TSRMLS_FETCH_FROM_CTX(state->ts); + + if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { - if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) { ++ if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) { + return FAILURE; + } + #ifdef PHP_HTTP_HAVE_WCHAR + } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { - if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len TSRMLS_CC)) { ++ if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) { + return FAILURE; + } + #endif + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; codepage not specified"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN; codepage not specified"); + return FAILURE; + } + + ahost_len = uidna_IDNToASCII(uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN, 3, NULL, &error); + efree(uhost_str); + + if (error != U_ZERO_ERROR) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; ICU error %d", error); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN; ICU error %d", error); + return FAILURE; + } + + host_ptr = state->url.host; + ahost_ptr = ahost_str; + PHP_HTTP_DUFF(ahost_len, *host_ptr++ = *ahost_ptr++); + + *host_ptr = '\0'; + state->offset += host_ptr - state->url.host; + + return SUCCESS; + } + #endif + + #if 0 && defined(PHP_WIN32) + static ZEND_RESULT_CODE parse_widn(struct parse_state *state) + { + char *host_ptr; + uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr; + size_t uhost_len; - TSRMLS_FETCH_FROM_CTX(state->ts); + + if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) { + if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN"); + return FAILURE; + } + #ifdef PHP_HTTP_HAVE_WCHAR + } else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) { + if (SUCCESS != to_utf16(parse_mb_loc, state->url.host, &uhost_str, &uhost_len)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN"); + return FAILURE; + } + #endif + } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN"); + return FAILURE; + } + + if (!IdnToAscii(IDN_ALLOW_UNASSIGNED|IDN_USE_STD3_ASCII_RULES, uhost_str, uhost_len, ahost_str, MAXHOSTNAMELEN)) { + efree(uhost_str); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN"); ++ php_error_docref(NULL, E_WARNING, "Failed to parse IDN"); + return FAILURE; + } + + efree(uhost_str); + host_ptr = state->url.host; + ahost_ptr = ahost_str; + PHP_HTTP_DUFF(wcslen(ahost_str), *host_ptr++ = *ahost_ptr++); + efree(ahost_str); + + *host_ptr = '\0'; + state->offset += host_ptr - state->url.host; + + return SUCCESS; + } + #endif + + #ifdef HAVE_INET_PTON + static const char *parse_ip6(struct parse_state *state, const char *ptr) + { ++ size_t mb, len; + const char *error = NULL, *end = state->ptr, *tmp = memchr(ptr, ']', end - ptr); - TSRMLS_FETCH_FROM_CTX(state->ts); + + if (tmp) { + size_t addrlen = tmp - ptr + 1; + char buf[16], *addr = estrndup(ptr + 1, addrlen - 2); + int rv = inet_pton(AF_INET6, addr, buf); + + if (rv == 1) { + state->buffer[state->offset] = '['; + state->url.host = &state->buffer[state->offset]; + inet_ntop(AF_INET6, buf, state->url.host + 1, state->maxlen - state->offset); + state->offset += strlen(state->url.host); + state->buffer[state->offset++] = ']'; + state->buffer[state->offset++] = 0; + ptr = tmp + 1; + } else if (rv == -1) { + error = strerror(errno); + } else { + error = "unexpected '['"; + } + efree(addr); + } else { + error = "expected ']'"; + } + + if (error) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse hostinfo; %s", error); ++ php_error_docref(NULL, E_WARNING, "Failed to parse hostinfo; %s", error); + return NULL; + } + + return ptr; + } + #endif + + static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *ptr) + { + size_t mb, len; + const char *end = state->ptr, *tmp = ptr, *port = NULL, *label = NULL; - TSRMLS_FETCH_FROM_CTX(state->ts); + + #ifdef HAVE_INET_PTON + if (*ptr == '[' && !(ptr = parse_ip6(state, ptr))) { + return FAILURE; + } + #endif + + if (ptr != end) do { + switch (*ptr) { + case ':': + if (port) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse port; unexpected ':' at pos %u in '%s'", + (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + port = ptr + 1; + break; + + case '%': + if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'", + (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + state->buffer[state->offset++] = *ptr++; + state->buffer[state->offset++] = *ptr++; + state->buffer[state->offset++] = *ptr; + break; + + case '!': case '$': case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case ';': case '=': /* sub-delims */ + case '-': case '.': case '_': case '~': /* unreserved */ + if (port || !label) { + /* sort of a compromise, just ensure we don't end up + * with a dot at the beginning or two consecutive dots + */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse %s; unexpected '%c' at pos %u in '%s'", + port ? "port" : "host", + (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + state->buffer[state->offset++] = *ptr; + label = NULL; + break; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + if (port) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse port; unexpected char '%c' at pos %u in '%s'", + (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp); + return FAILURE; + } + /* no break */ + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + /* allowed */ + if (port) { + state->url.port *= 10; + state->url.port += *ptr - '0'; + } else { + label = ptr; + state->buffer[state->offset++] = *ptr; + } + break; + + default: + if (ptr == end) { + break; + } else if (port) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'", + (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp); + return FAILURE; + } else if (!(mb = parse_mb(state, PARSE_HOSTINFO, ptr, end, tmp, 0))) { + return FAILURE; + } + label = ptr; + ptr += mb - 1; + } + } while (++ptr != end); + + if (!state->url.host) { + len = (port ? port - tmp - 1 : end - tmp); + state->url.host = &state->buffer[state->offset - len]; + state->buffer[state->offset++] = 0; + } + + if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) { + #if PHP_HTTP_HAVE_IDN2 + return parse_idn2(state, len); + #elif PHP_HTTP_HAVE_IDN + return parse_idn(state, len); + #endif + #ifdef HAVE_UIDNA_IDNTOASCII + return parse_uidn(state); + #endif + #if 0 && defined(PHP_WIN32) + return parse_widn(state); + #endif + } + + return SUCCESS; + } + + static const char *parse_authority(struct parse_state *state) + { + const char *tmp = state->ptr, *host = NULL; + + do { + switch (*state->ptr) { + case '@': + /* userinfo delimiter */ + if (host) { - TSRMLS_FETCH_FROM_CTX(state->ts); - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse userinfo; unexpected '@'"); + return NULL; + } + host = state->ptr + 1; + if (tmp != state->ptr && SUCCESS != parse_userinfo(state, tmp)) { + return NULL; + } + tmp = state->ptr + 1; + break; + + case '/': + case '?': + case '#': + case '\0': + EOD: + /* host delimiter */ + if (tmp != state->ptr && SUCCESS != parse_hostinfo(state, tmp)) { + return NULL; + } + return state->ptr; + } + } while (++state->ptr <= state->end); + + --state->ptr; + goto EOD; + } + + static const char *parse_path(struct parse_state *state) + { + size_t mb; + const char *tmp; - TSRMLS_FETCH_FROM_CTX(state->ts); + + /* is there actually a path to parse? */ + if (!*state->ptr) { + return state->ptr; + } + tmp = state->ptr; + state->url.path = &state->buffer[state->offset]; + + do { + switch (*state->ptr) { + case '#': + case '?': + goto done; + + case '%': + if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse path; invalid percent encoding at pos %u in '%s'", + (unsigned) (state->ptr - tmp), tmp); + return NULL; + } + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr; + break; + + case '/': /* yeah, well */ + case '!': case '$': case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case ';': case '=': /* sub-delims */ + case '-': case '.': case '_': case '~': /* unreserved */ + case ':': case '@': /* pchar */ + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + /* allowed */ + state->buffer[state->offset++] = *state->ptr; + break; + + default: + if (!(mb = parse_mb(state, PARSE_PATH, state->ptr, state->end, tmp, 0))) { + return NULL; + } + state->ptr += mb - 1; + } + } while (++state->ptr < state->end); + + done: + /* did we have any path component ? */ + if (tmp != state->ptr) { + state->buffer[state->offset++] = 0; + } else { + state->url.path = NULL; + } + return state->ptr; + } + + static const char *parse_query(struct parse_state *state) + { + size_t mb; + const char *tmp = state->ptr + !!*state->ptr; - TSRMLS_FETCH_FROM_CTX(state->ts); + + /* is there actually a query to parse? */ + if (*state->ptr != '?') { + return state->ptr; + } + + /* skip initial '?' */ + tmp = ++state->ptr; + state->url.query = &state->buffer[state->offset]; + + while (state->ptr < state->end) { + switch (*state->ptr) { + case '#': + goto done; + + case '%': + if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse query; invalid percent encoding at pos %u in '%s'", + (unsigned) (state->ptr - tmp), tmp); + return NULL; + } + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr; + break; + + /* RFC1738 unsafe */ + case '{': case '}': + case '<': case '>': + case '[': case ']': + case '|': case '\\': case '^': case '`': case '"': case ' ': + if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) { + state->buffer[state->offset++] = '%'; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4]; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf]; + break; + } + /* no break */ + + case '?': case '/': /* yeah, well */ + case '!': case '$': case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case ';': case '=': /* sub-delims */ + case '-': case '.': case '_': case '~': /* unreserved */ + case ':': case '@': /* pchar */ + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + /* allowed */ + state->buffer[state->offset++] = *state->ptr; + break; + + default: + if (!(mb = parse_mb(state, PARSE_QUERY, state->ptr, state->end, tmp, 0))) { + return NULL; + } + state->ptr += mb - 1; + } + + ++state->ptr; + } + + done: + state->buffer[state->offset++] = 0; + return state->ptr; + } + + static const char *parse_fragment(struct parse_state *state) + { + size_t mb; + const char *tmp; - TSRMLS_FETCH_FROM_CTX(state->ts); + + /* is there actually a fragment to parse? */ + if (*state->ptr != '#') { + return state->ptr; + } + + /* skip initial '#' */ + tmp = ++state->ptr; + state->url.fragment = &state->buffer[state->offset]; + + do { + switch (*state->ptr) { + case '%': + if (state->ptr[1] != '%' && (state->end - state->ptr <= 2 || !isxdigit(*(state->ptr+1)) || !isxdigit(*(state->ptr+2)))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse fragment; invalid percent encoding at pos %u in '%s'", + (unsigned) (state->ptr - tmp), tmp); + return NULL; + } + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr++; + state->buffer[state->offset++] = *state->ptr; + break; + + /* RFC1738 unsafe */ + case '{': case '}': + case '<': case '>': + case '[': case ']': + case '|': case '\\': case '^': case '`': case '"': case ' ': + if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) { + state->buffer[state->offset++] = '%'; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4]; + state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf]; + break; + } + /* no break */ + + case '?': case '/': + case '!': case '$': case '&': case '\'': case '(': case ')': case '*': + case '+': case ',': case ';': case '=': /* sub-delims */ + case '-': case '.': case '_': case '~': /* unreserved */ + case ':': case '@': /* pchar */ + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + /* allowed */ + state->buffer[state->offset++] = *state->ptr; + break; + + default: + if (!(mb = parse_mb(state, PARSE_FRAGMENT, state->ptr, state->end, tmp, 0))) { + return NULL; + } + state->ptr += mb - 1; + } + } while (++state->ptr < state->end); + + state->buffer[state->offset++] = 0; + return state->ptr; + } + + static const char *parse_hier(struct parse_state *state) + { + if (*state->ptr == '/') { + if (state->end - state->ptr > 1) { + if (*(state->ptr + 1) == '/') { + state->ptr += 2; + if (!(state->ptr = parse_authority(state))) { + return NULL; + } + } + } + } + return parse_path(state); + } + + static const char *parse_scheme(struct parse_state *state) + { + size_t mb; + const char *tmp = state->ptr; + + do { + switch (*state->ptr) { + case ':': + /* scheme delimiter */ + state->url.scheme = &state->buffer[0]; + state->buffer[state->offset++] = 0; + return ++state->ptr; + + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': + case '+': case '-': case '.': + if (state->ptr == tmp) { + return tmp; + } + /* no break */ + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + /* scheme part */ + state->buffer[state->offset++] = *state->ptr; + break; + + default: + if (!(mb = parse_mb(state, PARSE_SCHEME, state->ptr, state->end, tmp, 1))) { + /* soft fail; parse path next */ + return tmp; + } + state->ptr += mb - 1; + } + } while (++state->ptr != state->end); + + return state->ptr = tmp; + } + -php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC) ++php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags) + { + size_t maxlen = 3 * len; + struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen); + + state->end = str + len; + state->ptr = str; + state->flags = flags; + state->maxlen = maxlen; - TSRMLS_SET_CTX(state->ts); + + if (!parse_scheme(state)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr); ++ php_error_docref(NULL, E_WARNING, "Failed to parse URL scheme: '%s'", state->ptr); + efree(state); + return NULL; + } + + if (!parse_hier(state)) { + efree(state); + return NULL; + } + + if (!parse_query(state)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL query: '%s'", state->ptr); ++ php_error_docref(NULL, E_WARNING, "Failed to parse URL query: '%s'", state->ptr); + efree(state); + return NULL; + } + + if (!parse_fragment(state)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr); ++ php_error_docref(NULL, E_WARNING, "Failed to parse URL fragment: '%s'", state->ptr); + efree(state); + return NULL; + } + + return (php_http_url_t *) state; + } + -php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC) ++php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags) + { + size_t maxlen = 3 * len; + struct parse_state *state = ecalloc(1, sizeof(*state) + maxlen); + + state->end = str + len; + state->ptr = str; + state->flags = flags; + state->maxlen = maxlen; - TSRMLS_SET_CTX(state->ts); + + if (!(state->ptr = parse_authority(state))) { + efree(state); + return NULL; + } + + if (state->ptr != state->end) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, ++ php_error_docref(NULL, E_WARNING, + "Failed to parse URL authority, unexpected character at pos %u in '%s'", + (unsigned) (state->ptr - str), str); + efree(state); + return NULL; + } + + return (php_http_url_t *) state; + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0) + ZEND_ARG_INFO(0, old_url) + ZEND_ARG_INFO(0, new_url) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpUrl, __construct) + { + zval *new_url = NULL, *old_url = NULL; - long flags = PHP_HTTP_URL_FROM_ENV; ++ zend_long flags = PHP_HTTP_URL_FROM_ENV; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh); + { + php_http_url_t *res_purl, *new_purl = NULL, *old_purl = NULL; + + if (new_url) { - new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC); ++ new_purl = php_http_url_from_zval(new_url, flags); + if (!new_purl) { - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + return; + } + } + if (old_url) { - old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC); ++ old_purl = php_http_url_from_zval(old_url, flags); + if (!old_purl) { + if (new_purl) { + php_http_url_free(&new_purl); + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + return; + } + } + - res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); - php_http_url_to_struct(res_purl, getThis() TSRMLS_CC); ++ res_purl = php_http_url_mod(old_purl, new_purl, flags); ++ php_http_url_to_struct(res_purl, getThis()); + + php_http_url_free(&res_purl); + if (old_purl) { + php_http_url_free(&old_purl); + } + if (new_purl) { + php_http_url_free(&new_purl); + } + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod, 0, 0, 1) + ZEND_ARG_INFO(0, more_url_parts) + ZEND_ARG_INFO(0, flags) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpUrl, mod) + { + zval *new_url = NULL; - long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY | PHP_HTTP_URL_SANITIZE_PATH; ++ zend_long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY | PHP_HTTP_URL_SANITIZE_PATH; + zend_error_handling zeh; + - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return); ++ php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!|l", &new_url, &flags), invalid_arg, return); + - zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); ++ zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh); + { + php_http_url_t *new_purl = NULL, *old_purl = NULL; + + if (new_url) { - new_purl = php_http_url_from_zval(new_url, flags TSRMLS_CC); ++ new_purl = php_http_url_from_zval(new_url, flags); + if (!new_purl) { - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + return; + } + } + + if ((old_purl = php_http_url_from_struct(HASH_OF(getThis())))) { + php_http_url_t *res_purl; + - ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0); ++ ZVAL_OBJ(return_value, zend_objects_clone_obj(getThis())); + - res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); - php_http_url_to_struct(res_purl, return_value TSRMLS_CC); ++ res_purl = php_http_url_mod(old_purl, new_purl, flags); ++ php_http_url_to_struct(res_purl, return_value); + + php_http_url_free(&res_purl); + php_http_url_free(&old_purl); + } + if (new_purl) { + php_http_url_free(&new_purl); + } + } - zend_restore_error_handling(&zeh TSRMLS_CC); ++ zend_restore_error_handling(&zeh); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpUrl, toString) + { + if (SUCCESS == zend_parse_parameters_none()) { + php_http_url_t *purl; + + if ((purl = php_http_url_from_struct(HASH_OF(getThis())))) { + char *str; + size_t len; + + php_http_url_to_string(purl, &str, &len, 0); + php_http_url_free(&purl); - RETURN_STRINGL(str, len, 0); ++ RETURN_STR(php_http_cs2zs(str, len)); + } + } + RETURN_EMPTY_STRING(); + } + + ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray, 0, 0, 0) + ZEND_END_ARG_INFO(); + PHP_METHOD(HttpUrl, toArray) + { + php_http_url_t *purl; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + + /* strip any non-URL properties */ + purl = php_http_url_from_struct(HASH_OF(getThis())); - php_http_url_to_struct(purl, return_value TSRMLS_CC); ++ php_http_url_to_struct(purl, return_value); + php_http_url_free(&purl); + } + + static zend_function_entry php_http_url_methods[] = { + PHP_ME(HttpUrl, __construct, ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(HttpUrl, mod, ai_HttpUrl_mod, ZEND_ACC_PUBLIC) + PHP_ME(HttpUrl, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC) + ZEND_MALIAS(HttpUrl, __toString, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC) + PHP_ME(HttpUrl, toArray, ai_HttpUrl_toArray, ZEND_ACC_PUBLIC) + EMPTY_FUNCTION_ENTRY + }; + + zend_class_entry *php_http_url_class_entry; + + PHP_MINIT_FUNCTION(http_url) + { + zend_class_entry ce = {0}; + + INIT_NS_CLASS_ENTRY(ce, "http", "Url", php_http_url_methods); - php_http_url_class_entry = zend_register_internal_class(&ce TSRMLS_CC); - - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC); - zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC); - - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC); - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC); ++ php_http_url_class_entry = zend_register_internal_class(&ce); ++ ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("path"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC); ++ zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC); ++ ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH); + + #ifdef PHP_HTTP_HAVE_WCHAR - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC); + #endif - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8); + #if defined(PHP_HTTP_HAVE_IDN2) || defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII) - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN); + #endif - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC); ++ zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT); + + return SUCCESS; + } + + + /* + * 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 --cc src/php_http_url.h index 0000000,636efb5..6ae0ac3 mode 000000,100644..100644 --- a/src/php_http_url.h +++ b/src/php_http_url.h @@@ -1,0 -1,99 +1,102 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_URL_H + #define PHP_HTTP_URL_H + + #include + + /* php_http_url_mod() */ + #define PHP_HTTP_URL_REPLACE 0x000 + #define PHP_HTTP_URL_JOIN_PATH 0x001 + #define PHP_HTTP_URL_JOIN_QUERY 0x002 + #define PHP_HTTP_URL_STRIP_USER 0x004 + #define PHP_HTTP_URL_STRIP_PASS 0x008 + #define PHP_HTTP_URL_STRIP_AUTH (PHP_HTTP_URL_STRIP_USER|PHP_HTTP_URL_STRIP_PASS) + #define PHP_HTTP_URL_STRIP_PORT 0x020 + #define PHP_HTTP_URL_STRIP_PATH 0x040 + #define PHP_HTTP_URL_STRIP_QUERY 0x080 + #define PHP_HTTP_URL_STRIP_FRAGMENT 0x100 + #define PHP_HTTP_URL_STRIP_ALL ( \ + PHP_HTTP_URL_STRIP_AUTH | \ + PHP_HTTP_URL_STRIP_PORT | \ + PHP_HTTP_URL_STRIP_PATH | \ + PHP_HTTP_URL_STRIP_QUERY | \ + PHP_HTTP_URL_STRIP_FRAGMENT \ + ) + #define PHP_HTTP_URL_FROM_ENV 0x1000 + #define PHP_HTTP_URL_SANITIZE_PATH 0x2000 + + /* parse multibyte according to locale */ + #define PHP_HTTP_URL_PARSE_MBLOC 0x10000 + /* parse utf8 multibyte sequences */ + #define PHP_HTTP_URL_PARSE_MBUTF8 0x20000 + /* convert multibyte hostnames to IDNA */ + #define PHP_HTTP_URL_PARSE_TOIDN 0x100000 + /* percent encode multibyte sequences in userinfo, path, query and fragment */ + #define PHP_HTTP_URL_PARSE_TOPCT 0x200000 + + typedef struct php_http_url { + /* compatible to php_url, but do not use php_url_free() */ + char *scheme; + char *user; + char *pass; + char *host; + unsigned short port; + char *path; + char *query; + char *fragment; + } php_http_url_t; + -PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags TSRMLS_DC); -PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags TSRMLS_DC); -PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags TSRMLS_DC); ++PHP_HTTP_API php_http_url_t *php_http_url_parse(const char *str, size_t len, unsigned flags); ++PHP_HTTP_API php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsigned flags); ++PHP_HTTP_API php_http_url_t *php_http_url_mod(const php_http_url_t *old_url, const php_http_url_t *new_url, unsigned flags); + PHP_HTTP_API php_http_url_t *php_http_url_copy(const php_http_url_t *url, zend_bool persistent); + PHP_HTTP_API php_http_url_t *php_http_url_from_struct(HashTable *ht); -PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags TSRMLS_DC); -PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC); ++PHP_HTTP_API php_http_url_t *php_http_url_from_zval(zval *value, unsigned flags); ++PHP_HTTP_API HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct); + PHP_HTTP_API char *php_http_url_to_string(const php_http_url_t *url, char **url_str, size_t *url_len, zend_bool persistent); + PHP_HTTP_API char *php_http_url_authority_to_string(const php_http_url_t *url, char **url_str, size_t *url_len); + PHP_HTTP_API void php_http_url_free(php_http_url_t **url); + -PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len TSRMLS_DC); -PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len TSRMLS_DC); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len); ++PHP_HTTP_API ZEND_RESULT_CODE php_http_url_encode_hash_ex(HashTable *hash, php_http_buffer_t *qstr, const char *arg_sep_str, size_t arg_sep_len, const char *val_sep_str, size_t val_sep_len, const char *pre_encoded_str, size_t pre_encoded_len); + -static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC) ++static inline void php_http_url_argsep(const char **str, size_t *len) + { - php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC); ++ if (SUCCESS != php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0) || !*len) { ++ *str = PHP_HTTP_URL_ARGSEP; ++ *len = lenof(PHP_HTTP_URL_ARGSEP); ++ } + } + + static inline zend_bool php_http_url_is_empty(const php_http_url_t *url) { + return !(url->scheme || url->pass || url->user || url->host || url->port || url->path || url->query || url->fragment); + } + + PHP_HTTP_API zend_class_entry *php_http_url_class_entry; + PHP_MINIT_FUNCTION(http_url); + + #define php_http_url_object_new php_http_object_new + #define php_http_url_object_new_ex php_http_object_new_ex + + #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 --cc src/php_http_version.c index 0000000,7adef9d..e763a85 mode 000000,100644..100644 --- a/src/php_http_version.c +++ b/src/php_http_version.c @@@ -1,0 -1,90 +1,90 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #include "php_http_api.h" + -php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC) ++php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor) + { + if (!v) { + v = emalloc(sizeof(*v)); + } + + v->major = major; + v->minor = minor; + + return v; + } + -php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC) ++php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str) + { + long major, minor; + char separator = 0; + register const char *ptr = str; + + switch (*ptr) { + case 'h': + case 'H': + ++ptr; if (*ptr != 't' && *ptr != 'T') break; + ++ptr; if (*ptr != 't' && *ptr != 'T') break; + ++ptr; if (*ptr != 'p' && *ptr != 'P') break; + ++ptr; if (*ptr != '/') break; + ++ptr; + /* no break */ + default: + /* rfc7230#2.6 The HTTP version number consists of two decimal digits separated by a "." (period or decimal point) */ + major = *ptr++ - '0'; + if (major >= 0 && major <= 9) { + separator = *ptr++; + if (separator) { + if (separator != '.' && separator != ',') { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr - 2); ++ php_error_docref(NULL, E_NOTICE, "Non-standard version separator '%c' in HTTP protocol version '%s'", separator, ptr - 2); + } + minor = *ptr - '0'; + if (minor >= 0 && minor <= 9) { - return php_http_version_init(v, major, minor TSRMLS_CC); ++ return php_http_version_init(v, major, minor); + } + } + } + } + - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not parse HTTP protocol version '%s'", str); ++ php_error_docref(NULL, E_WARNING, "Could not parse HTTP protocol version '%s'", str); + return NULL; + } + -void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC) ++void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post) + { + *len = spprintf(str, 0, "%s%u.%u%s", pre ? pre : "", v->major, v->minor, post ? post : ""); + } + + void php_http_version_dtor(php_http_version_t *v) + { + (void) v; + } + + void php_http_version_free(php_http_version_t **v) + { + if (*v) { + php_http_version_dtor(*v); + efree(*v); + *v = 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 --cc src/php_http_version.h index 0000000,40b833e..6f51a47 mode 000000,100644..100644 --- a/src/php_http_version.h +++ b/src/php_http_version.h @@@ -1,0 -1,38 +1,38 @@@ + /* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2014, Michael Wallner | + +--------------------------------------------------------------------+ + */ + + #ifndef PHP_HTTP_VERSION_H + #define PHP_HTTP_VERSION_H + + typedef struct php_http_version { + unsigned major; + unsigned minor; + } php_http_version_t; + -PHP_HTTP_API php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor TSRMLS_DC); -PHP_HTTP_API php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str TSRMLS_DC); -PHP_HTTP_API void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post TSRMLS_DC); ++PHP_HTTP_API php_http_version_t *php_http_version_init(php_http_version_t *v, unsigned major, unsigned minor); ++PHP_HTTP_API php_http_version_t *php_http_version_parse(php_http_version_t *v, const char *str); ++PHP_HTTP_API void php_http_version_to_string(php_http_version_t *v, char **str, size_t *len, const char *pre, const char *post); + PHP_HTTP_API void php_http_version_dtor(php_http_version_t *v); + PHP_HTTP_API void php_http_version_free(php_http_version_t **v); + + #endif /* PHP_HTTP_VERSION_H */ + + + /* + * 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 + */ +