From: Michael Wallner Date: Mon, 28 Sep 2015 14:08:14 +0000 (+0200) Subject: attempt to implement some personal standards X-Git-Tag: RELEASE_2_5_5~6 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=bdd6edb59194cda9e5fcb393c48ab4230fceb32a attempt to implement some personal standards --- diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e0c3b0e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; see http://editorconfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +charset = utf-8 +trim_trailing_whitespace = true + +[package.xml] +indent_style = space +indent_size = 1 + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index c78e2b8..fc8d761 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ tests/*.sh lcov_data *~ *.phar +vendor/ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..67bbd91 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Michael Wallner diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..4c2ad80 --- /dev/null +++ b/BUGS @@ -0,0 +1,25 @@ +Known Issues +============ + +Windows: + If you keep getting "SSL connect error" when trying to issue + requests, try another (newer) libeay32.dll/ssleay32.dll pair. + +Internals: + Inflating raw deflated data causes a re-initialization of the inflate + stream where the corresponding window bits are modified to tell libz + to not check for zlib header bytes. This is not preventable AFAICS. + LFS dependant parts of libcurl are left out because of off_t, + respectively off64_t confusion. + Persistent handles and "cookiestore" request option do interfere, + as libcurl saves the cookies to the file on curl_easy_destroy(), + cookies are not saved until the CURL handle will be recycled. + Thus one would either need to + * run PHP with raphf.persistent_handles.limit = 0 + * call raphf\persistent_handles_clean() every request + * call $client->flushCookies(), which is available + since libcurl v7.17.1 and does not work with the + procedural API + HTTP and Proxy authentication information (username/password) can not be + unset with NULL prior libcurl v7.19.6 and separate options for setting + username and password--which work--are only available since v7.19.6. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..968bd44 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of +fostering an open and welcoming community, we pledge to respect all people who +contribute through reporting issues, posting feature requests, updating +documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery +* Personal attacks +* Trolling or insulting/derogatory comments +* Public or private harassment +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission +* Other unethical or unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct. By adopting this Code of Conduct, project +maintainers commit themselves to fairly and consistently applying these +principles to every aspect of managing this project. Project maintainers who do +not follow or enforce the Code of Conduct may be permanently removed from the +project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the +[Contributor Covenant](http://contributor-covenant.org), version 1.2.0, +available at http://contributor-covenant.org/version/1/2/0/. diff --git a/Exceptions.txt b/Exceptions.txt deleted file mode 100644 index f666ee9..0000000 --- a/Exceptions.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Throw Exceptions: - -* on setters, that return self -* on getters, that return objects diff --git a/KnownIssues.txt b/KnownIssues.txt deleted file mode 100644 index 4c2ad80..0000000 --- a/KnownIssues.txt +++ /dev/null @@ -1,25 +0,0 @@ -Known Issues -============ - -Windows: - If you keep getting "SSL connect error" when trying to issue - requests, try another (newer) libeay32.dll/ssleay32.dll pair. - -Internals: - Inflating raw deflated data causes a re-initialization of the inflate - stream where the corresponding window bits are modified to tell libz - to not check for zlib header bytes. This is not preventable AFAICS. - LFS dependant parts of libcurl are left out because of off_t, - respectively off64_t confusion. - Persistent handles and "cookiestore" request option do interfere, - as libcurl saves the cookies to the file on curl_easy_destroy(), - cookies are not saved until the CURL handle will be recycled. - Thus one would either need to - * run PHP with raphf.persistent_handles.limit = 0 - * call raphf\persistent_handles_clean() every request - * call $client->flushCookies(), which is available - since libcurl v7.17.1 and does not work with the - procedural API - HTTP and Proxy authentication information (username/password) can not be - unset with NULL prior libcurl v7.19.6 and separate options for setting - username and password--which work--are only available since v7.19.6. diff --git a/LICENSE b/LICENSE index 786ba27..c7d1587 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2014, Michael Wallner . +Copyright (c) 2004-2015, Michael Wallner . All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Makefile.frag b/Makefile.frag new file mode 100644 index 0000000..3ef9ecb --- /dev/null +++ b/Makefile.frag @@ -0,0 +1,16 @@ +# provide headers in builddir, so they do not end up in /usr/include/ext/http/src + +PHP_HTTP_HEADERS := $(addprefix $(PHP_HTTP_BUILDDIR)/,$(PHP_HTTP_HEADERS)) + +$(PHP_HTTP_BUILDDIR)/%.h: $(PHP_HTTP_SRCDIR)/src/%.h + @cat >$@ <$< + +all: http-build-headers +clean: http-clean-headers + +.PHONY: http-build-headers +http-build-headers: $(PHP_HTTP_HEADERS) + +.PHONY: http-clean-headers +http-clean-headers: + -rm $(PHP_HTTP_HEADERS) diff --git a/README.md b/README.md index c0ee4d1..19738da 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,48 @@ -# pecl/http v2 +# ext-http -[![Build Status](https://travis-ci.org/m6w6/ext-http.svg?branch=R_2_5)](https://travis-ci.org/m6w6/ext-http) +[![Build Status](https://travis-ci.org/m6w6/ext-http.svg?branch=master)](https://travis-ci.org/m6w6/ext-http) -## About: +Extended HTTP support. Again. -Extended HTTP support. Again. +## Documentation -* Introduces the http namespace. -* PHP stream based message bodies. -* Encapsulated env request/response. -* Modular client support. +See the [online markdown reference](https://mdref.m6w6.name/http). -## Installation: +Known issues are listed in [BUGS](./BUGS) and future ideas can be found in [TODO](./TODO). -This extension is hosted at [PECL](http://pecl.php.net) and can be installed with [PEAR](http://pear.php.net)'s pecl command: +## Installing - # pecl install pecl_http +### PECL -## Dependencies: + pecl install pecl_http -pecl/http depends on a number of system libraries and PHP extensions for special features. +### PHARext -#### Required system libraries: +Watch out for [PECL replicates](https://replicator.pharext.org?pecl_http) +and pharext packages attached to [releases](./releases). -The following system libraries are required to build this extension: +### Checkout -##### zlib -Provides gzip/zlib/deflate encoding. -Minimum version: 1.2.0.4 -Install on Debian: `apt-get install zlib1g-dev` + git clone github.com:m6w6/ext-http + cd ext-http + /path/to/phpize + ./configure --with-php-config=/path/to/php-config + make + sudo make install +## ChangeLog -#### Optional system libraries: +A comprehensive list of changes can be obtained from the +[PECL website](https://pecl.php.net/package-changelog.php?package=pecl_http). -The following system libraries are optional and provide additional features: +## License -##### libidn -Provides IDNA support in URLs. -Minimum version: none -Install on Debian: `apt-get install libidn11-dev` +ext-http is licensed under the 2-Clause-BSD license, which can be found in +the accompanying [LICENSE](./LICENSE) file. -##### libidn2 -Provides IDNA support in URLs (fallback if libidn is not available). -Minimum version: none -Install on Debian: `apt-get install libidn2-0-dev` +## Contributing -##### libicu -Provides IDNA support in URLs (fallback if libidn is not available). -Minimum version: none -Install on Debian: `apt-get install libicu-dev` +All forms of contribution are welcome! Please see the bundled +[CONTRIBUTING](./CONTRIBUTING.md) note for the general principles followed. -##### libcurl -Provides HTTP request functionality. -Minimum version: 7.18.2 -Install on Debian: `apt-get install libcurl4-openssl-dev` -Note: There are usually different styles of SSL support for libcurl available, so you can replace 'openssl' in the above command f.e. with 'nss' or 'gnutls'. - -##### libevent -Eventloop support for the HTTP client. -Minimum version: none -Install on Debian: `apt-get install libevent-dev` - -### PHP extensions: - -This extension unconditionally depends on the pre-loaded presence of the following PHP extensions: - -* [raphf](https://github.com/m6w6/ext-raphf) -* [propro](https://github.com/m6w6/ext-propro) -* spl - - -If configured ```--with-http-shared-deps``` (default) it depends on the pre-loaded presence of the following extensions, as long as they were available at build time: - -* hash -* iconv -* json (only until < 2.4.0) - -Please ensure that all extension on which pecl/http depends, are loaded before it, e.g in your `php.ini`: - - ; obligatory deps - extension = raphf.so - extension = propro.so - - ; if shared deps were enabled - extension = hash.so - extension = iconv.so - extension = json.so - - ; finally load pecl/http - extension = http.so - -## Conflicts: - -pecl/http-v2 conflicts with the following extensions: - -* http-v1 -* event (only until <= 2.0.3) - -## INI Directives: - -* http.etag.mode = "crc32b" - Default hash method for dynamic response payloads to generate an ETag. - -## Stream Filters: - -The http extension registers the ```http.*``` namespace for its stream filters. Provided stream filters are: - -* http.chunked_decode - Decode a stream encoded with chunked transfer encoding. -* http.chunked_encode - Encode a stream with chunked transfer encoding. -* http.inflate - Decode a stream encoded with deflate/zlib/gzip encoding. -* http.deflate - Encode a stream with deflate/zlib/gzip encoding. - - -## Documentation: - -Documentation is available at https://mdref.m6w6.name/http +The list of past and current contributors is maintained in [THANKS](./THANKS). diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..d5ddcb5 --- /dev/null +++ b/THANKS @@ -0,0 +1,15 @@ +Thanks go to the following people, who have contributed to this project: + + Ilia Alshanetsky (ilia at php dot net) + Anatol Belski (ab at php dot net) + Petr Czaderna (petr at hroch dot info) + Remi Collet (remi at php dot net) + Benjamin Eberlei (kontakt at beberlei dot de) + David James (james82 at gmail dot com) + Thomas Landro Johnsen (thomas dot l dot johnsen at gmail dot com) + Clay Loveless (clay at killersoft dot com) + Felipe Pena (felipe at php dot net) + David Sklar (sklar at sklar dot com) + Travis Swicegood (travis at mashery dot com) + Alexey Zakhlestin (indeyets at gmail dot com) + Alexander Zhuravlev (zaa at zaa dot pp dot ru) diff --git a/ThanksTo.txt b/ThanksTo.txt deleted file mode 100644 index 67c5b32..0000000 --- a/ThanksTo.txt +++ /dev/null @@ -1,22 +0,0 @@ -Thanks To -========= - -People who repeatedly reported issues with this extension in a manner -so they could be fixed in a reasonable way, or suggested useful features -to implement, in alphabetical order: - - Ilia Alshanetsky (ilia at php dot net) - Anatol Belski (ab at php dot net) - Petr Czaderna (petr at hroch dot info) - Remi Collet (remi at php dot net) - Benjamin Eberlei (kontakt at beberlei dot de) - David James (james82 at gmail dot com) - Thomas Landro Johnsen (thomas dot l dot johnsen at gmail dot com) - Clay Loveless (clay at killersoft dot com) - Felipe Pena (felipe at php dot net) - David Sklar (sklar at sklar dot com) - Travis Swicegood (travis at mashery dot com) - Alexey Zakhlestin (indeyets at gmail dot com) - Alexander Zhuravlev (zaa at zaa dot pp dot ru) - -Thanks a lot! diff --git a/bench_select_vs_event.php b/bench_select_vs_event.php deleted file mode 100644 index 3bc9aed..0000000 --- a/bench_select_vs_event.php +++ /dev/null @@ -1,53 +0,0 @@ - -n -c [-p (enable pipelining)] [-e (use libevent)]\n", $argv[0]); - fprintf(STDERR, "\nDefaults: -u http://localhost/ -n 1000 -c 10\n\n"); - exit(-1); -} - -function push($client, $url, &$n) { - if ($n-- > 0) { - $req = new http\Client\Request("GET", $url); - $client->enqueue($req, function($response) use ($client, $req, $url, &$n) { - global $count; ++$count; - push($client, $url, $n); - return true; // dequeue - }); - } -} - -isset($argv) or $argv = $_SERVER['argv']; -defined('STDERR') or define('STDERR', fopen('php://stderr', 'w')); - -$opts = getopt("u:c:n:e"); -isset($opts["u"]) or $opts["u"] = "http://localhost/"; -isset($opts["c"]) or $opts["c"] = 10; -isset($opts["n"]) or $opts["n"] = 1000; - -$argc > 1 or usage(); - -$time = microtime(true); -$count = 0; -$client = new http\Client; - -$client->enablePipelining($opts["p"]===false); -$client->enableEvents($opts["e"]===false); - -for ($i = 0, $x = $opts["n"]; $i < $opts["c"]; ++$i) { - push($client, $opts["u"], $x); -} - -try { - $client->send(); -} catch (Exception $e) { - echo $e; -} - -printf("\n> %10.6fs (%3.2fM)\n", microtime(true)-$time, memory_get_peak_usage(true)/1024/1024); - -$count == $opts["n"] or printf("\nOnly %d finished\n", $count); diff --git a/check_package-xml.php b/check_package-xml.php deleted file mode 100755 index 32a1e73..0000000 --- a/check_package-xml.php +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env php - 1) { - if ($argv[1] === "-") { - $file = "php://stdin"; - } else { - $file = $argv[1]; - } -} elseif (stdin_is_readable()) { - $file = "php://stdin"; -} else { - $file = "./package.xml"; -} - -if (($xml = simplexml_load_file($file))) { - $xml_files = xmllist($xml->contents[0]); - $dirs = ["."]; - while ($dir = array_shift($dirs)) { - foreach (dirlist($dir) as $file) { - if (is_gitignored($file)) { - continue; - } - if (!is_dir($file)) { - if (!in_array($file, $xml_files)) { - echo "Missing file $file\n"; - } - } else { - $base = basename($file); - if ($base{0} !== ".") { - array_push($dirs, $file); - } - } - } - } - foreach ($xml_files as $file) { - if (!file_exists($file)) { - echo "Extraneous file $file\n"; - } - } -} - -### - -function error($fmt) { - trigger_error(call_user_func_array("sprintf", func_get_args())); -} - -function stdin_is_readable() { - $r = [STDIN]; $w = $e = []; - return stream_select($r, $w, $e, 0); -} - -function is_gitignored($file) { - static $gitignore; - - if (!isset($gitignore)) { - if (is_readable(".gitignore")) { - $gitignore = explode("\n", `find | git check-ignore --stdin`); - } else { - $gitignore = false; - } - } - if ($gitignore) { - return in_array($file, $gitignore); - } - return false; -} - -function xmllist(SimpleXmlElement $dir, $p = ".", &$a = null) { - settype($a, "array"); - $p = trim($p, "/") . "/" . trim($dir["name"], "/") . "/"; - foreach ($dir as $file) { - switch ($file->getName()) { - case "dir": - xmllist($file, $p, $a); - break; - case "file": - $a[] = sprintf("%s/%s", trim($p, "/"), trim($file["name"])); - break; - default: - error("Unknown content type: %s", $file->getName()); - break; - } - } - return $a; -} - -function dirlist($dir, $p = null) { - $p = implode("/", array_filter([trim($p, "/"), trim($dir, "/")])); - foreach (scandir($p) as $file) { - yield $p."/".$file; - } -} diff --git a/config.w32 b/config.w32 index 13c24ab..4bd1f69 100644 --- a/config.w32 +++ b/config.w32 @@ -1,131 +1,131 @@ -// config.w32 for pecl/http -// $Id$ - -ARG_ENABLE("http", "whether to enable extended HTTP support", "no"); - -function check_for_main_ext(ext, header) -{ - if (!header) { - header = "php_"+ ext +".h"; - } - - /* When in configure, we're always in the root of PHP source */ - var ext_path = "ext\\" + ext; - - STDOUT.Write("Checking for ext/"+ ext +" ... "); - - if (FSO.FileExists(ext_path + "\\" + header)) { - STDOUT.WriteLine(ext_path); - return ext_path; - } - - STDOUT.WriteLine(""); - return false; -} - -function check_for_pecl_ext(ext, header) -{ - if (!header) { - header = "php_"+ ext +".h"; - } - - var g; - var s = ext +"\\"+ header; - - STDOUT.Write("Checking for pecl/"+ ext +" ... "); - if ( (g = glob(configure_module_dirname +"\\..\\"+ s)) || - (g = glob(configure_module_dirname +"\\..\\..\\..\\pecl\\"+ s))) { - var f = g[0].substr(0, g[0].length - header.length - 1); - STDOUT.WriteLine(f); - return f; - } - STDOUT.WriteLine(""); - return false; -} - -if (PHP_HTTP != "no") { - - EXTENSION("http", - "php_http.c php_http_buffer.c php_http_client.c " + - "php_http_client_request.c php_http_client_response.c " + - "php_http_cookie.c php_http_curl.c php_http_client_curl.c " + - "php_http_encoding.c php_http_env.c php_http_env_request.c " + - "php_http_env_response.c php_http_etag.c php_http_exception.c php_http_filter.c php_http_header_parser.c " + - "php_http_header.c php_http_info.c php_http_message.c php_http_message_body.c php_http_message_parser.c " + - "php_http_misc.c php_http_negotiate.c php_http_object.c php_http_options.c php_http_params.c " + - "php_http_querystring.c php_http_url.c php_http_version.c", - null, - null); - AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support"); - AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions"); - - AC_DEFINE("HAVE_GETHOSTNAME", 1); - - if (PHP_DEBUG != "no") { - ADD_FLAG("CFLAGS_HTTP", "/W3"); - } - - if (CHECK_HEADER_ADD_INCLUDE('zlib.h', 'CFLAGS_HTTP', '..\\zlib;' + php_usual_include_suspects)) { - AC_DEFINE('HTTP_HAVE_ZLIB', 1, "Have zlib library"); - ADD_FLAG("LDFLAGS_HTTP", "/FORCE:MULTIPLE"); - } else { - WARNING("zlib encoding functions not enabled; libraries and headers not found"); - } - - if (typeof(PHP_HASH) != "undefined" && PHP_HASH != "no") { - var f; - - if ((f = check_for_pecl_ext("hash")) || (f = check_for_main_ext("hash"))) { - ADD_FLAG("CFLAGS_HTTP", '/I "' + f + '" /DHTTP_HAVE_PHP_HASH_H=1'); - ADD_EXTENSION_DEP("http", "hash", true); - } - } - - if (PHP_SESSION != "no") { - ADD_EXTENSION_DEP("http", "session", true); - } - - if (PHP_ICONV != "no") { - ADD_EXTENSION_DEP("http", "iconv", true); - } - - if (PHP_CURL != "no") { - ADD_EXTENSION_DEP("http", "curl", true); - } - - - CURL_LIB="libcurl_a.lib;libcurl.lib;" + (PHP_DEBUG != "no" ? "libcurld.lib":"libcurl.lib"); - if (CHECK_HEADER_ADD_INCLUDE("curl/curl.h", "CFLAGS_HTTP") && - CHECK_HEADER_ADD_INCLUDE("openssl/crypto.h", "CFLAGS_HTTP") && - CHECK_LIB(CURL_LIB, "http", PHP_HTTP) && - CHECK_LIB("ssleay32.lib", "http", PHP_HTTP) && - CHECK_LIB("libeay32.lib", "http", PHP_HTTP) && - CHECK_LIB("zlib.lib;zlib_a.lib", "http", PHP_HTTP) && - CHECK_LIB("libcurl_a.lib", "http", PHP_HTTP) && - ADD_EXTENSION_DEP("http", "propro", true) && - ADD_EXTENSION_DEP("http", "raphf", true) && - CHECK_LIB("winmm.lib", "http", PHP_HTTP)) { - AC_DEFINE("PHP_HTTP_HAVE_CURL", 1, "Have CURL library"); - AC_DEFINE("PHP_HTTP_HAVE_SSL", 1, "Have SSL"); - AC_DEFINE("PHP_HAVE_CURL_MULTI_STRERROR", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_SHARE_STRERROR", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_EASY_STRERROR", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_EASY_RESET", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_GETFORMDATA", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_FORMGET", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_MULTI_SETOPT", 1, ""); - AC_DEFINE("PHP_HAVE_CURL_MULTI_TIMEOUT", 1, ""); - - if (CHECK_HEADER_ADD_INCLUDE("event2/event.h", "CFLAGS_HTTP") && - CHECK_LIB("libevent.lib", "http", PHP_HTTP) && - CHECK_LIB("libevent_core.lib", "http", PHP_HTTP) && - CHECK_LIB("libevent_extras.lib", "http", PHP_HTTP)) { - - AC_DEFINE("PHP_HTTP_HAVE_EVENT", 1); - AC_DEFINE("PHP_HTTP_HAVE_EVENT2", 1); - AC_DEFINE("PHP_HTTP_EVENT_VERSION", "2.0.21 or greater"); - } - } else { - WARNING("curl convenience functions not enabled; libraries and headers not found"); - } -} +// config.w32 for pecl/http +// $Id$ + +ARG_ENABLE("http", "whether to enable extended HTTP support", "no"); + +function check_for_main_ext(ext, header) +{ + if (!header) { + header = "php_"+ ext +".h"; + } + + /* When in configure, we're always in the root of PHP source */ + var ext_path = "ext\\" + ext; + + STDOUT.Write("Checking for ext/"+ ext +" ... "); + + if (FSO.FileExists(ext_path + "\\" + header)) { + STDOUT.WriteLine(ext_path); + return ext_path; + } + + STDOUT.WriteLine(""); + return false; +} + +function check_for_pecl_ext(ext, header) +{ + if (!header) { + header = "php_"+ ext +".h"; + } + + var g; + var s = ext +"\\"+ header; + + STDOUT.Write("Checking for pecl/"+ ext +" ... "); + if ( (g = glob(configure_module_dirname +"\\..\\"+ s)) || + (g = glob(configure_module_dirname +"\\..\\..\\..\\pecl\\"+ s))) { + var f = g[0].substr(0, g[0].length - header.length - 1); + STDOUT.WriteLine(f); + return f; + } + STDOUT.WriteLine(""); + return false; +} + +if (PHP_HTTP != "no") { + + EXTENSION("http", + "src/php_http.c src/php_http_buffer.c src/php_http_client.c " + + "src/php_http_client_request.c src/php_http_client_response.c " + + "src/php_http_cookie.c src/php_http_curl.c src/php_http_client_curl.c " + + "src/php_http_encoding.c src/php_http_env.c src/php_http_env_request.c " + + "src/php_http_env_response.c src/php_http_etag.c src/php_http_exception.c src/php_http_filter.c src/php_http_header_parser.c " + + "src/php_http_header.c src/php_http_info.c src/php_http_message.c src/php_http_message_body.c src/php_http_message_parser.c " + + "src/php_http_misc.c src/php_http_negotiate.c src/php_http_object.c src/php_http_options.c src/php_http_params.c " + + "src/php_http_querystring.c src/php_http_url.c src/php_http_version.c", + null, + null); + AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support"); + AC_DEFINE("HTTP_SHARED_DEPS", 1, "Depend on shared extensions"); + + AC_DEFINE("HAVE_GETHOSTNAME", 1); + + if (PHP_DEBUG != "no") { + ADD_FLAG("CFLAGS_HTTP", "/W3"); + } + + if (CHECK_HEADER_ADD_INCLUDE('zlib.h', 'CFLAGS_HTTP', '..\\zlib;' + php_usual_include_suspects)) { + AC_DEFINE('HTTP_HAVE_ZLIB', 1, "Have zlib library"); + ADD_FLAG("LDFLAGS_HTTP", "/FORCE:MULTIPLE"); + } else { + WARNING("zlib encoding functions not enabled; libraries and headers not found"); + } + + if (typeof(PHP_HASH) != "undefined" && PHP_HASH != "no") { + var f; + + if ((f = check_for_pecl_ext("hash")) || (f = check_for_main_ext("hash"))) { + ADD_FLAG("CFLAGS_HTTP", '/I "' + f + '" /DHTTP_HAVE_PHP_HASH_H=1'); + ADD_EXTENSION_DEP("http", "hash", true); + } + } + + if (PHP_SESSION != "no") { + ADD_EXTENSION_DEP("http", "session", true); + } + + if (PHP_ICONV != "no") { + ADD_EXTENSION_DEP("http", "iconv", true); + } + + if (PHP_CURL != "no") { + ADD_EXTENSION_DEP("http", "curl", true); + } + + + CURL_LIB="libcurl_a.lib;libcurl.lib;" + (PHP_DEBUG != "no" ? "libcurld.lib":"libcurl.lib"); + if (CHECK_HEADER_ADD_INCLUDE("curl/curl.h", "CFLAGS_HTTP") && + CHECK_HEADER_ADD_INCLUDE("openssl/crypto.h", "CFLAGS_HTTP") && + CHECK_LIB(CURL_LIB, "http", PHP_HTTP) && + CHECK_LIB("ssleay32.lib", "http", PHP_HTTP) && + CHECK_LIB("libeay32.lib", "http", PHP_HTTP) && + CHECK_LIB("zlib.lib;zlib_a.lib", "http", PHP_HTTP) && + CHECK_LIB("libcurl_a.lib", "http", PHP_HTTP) && + ADD_EXTENSION_DEP("http", "propro", true) && + ADD_EXTENSION_DEP("http", "raphf", true) && + CHECK_LIB("winmm.lib", "http", PHP_HTTP)) { + AC_DEFINE("PHP_HTTP_HAVE_CURL", 1, "Have CURL library"); + AC_DEFINE("PHP_HTTP_HAVE_SSL", 1, "Have SSL"); + AC_DEFINE("PHP_HAVE_CURL_MULTI_STRERROR", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_SHARE_STRERROR", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_EASY_STRERROR", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_EASY_RESET", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_GETFORMDATA", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_FORMGET", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_MULTI_SETOPT", 1, ""); + AC_DEFINE("PHP_HAVE_CURL_MULTI_TIMEOUT", 1, ""); + + if (CHECK_HEADER_ADD_INCLUDE("event2/event.h", "CFLAGS_HTTP") && + CHECK_LIB("libevent.lib", "http", PHP_HTTP) && + CHECK_LIB("libevent_core.lib", "http", PHP_HTTP) && + CHECK_LIB("libevent_extras.lib", "http", PHP_HTTP)) { + + AC_DEFINE("PHP_HTTP_HAVE_EVENT", 1); + AC_DEFINE("PHP_HTTP_HAVE_EVENT2", 1); + AC_DEFINE("PHP_HTTP_EVENT_VERSION", "2.0.21 or greater"); + } + } else { + WARNING("curl convenience functions not enabled; libraries and headers not found"); + } +} diff --git a/config9.m4 b/config9.m4 index 273f038..da682f7 100644 --- a/config9.m4 +++ b/config9.m4 @@ -31,22 +31,22 @@ if test "$PHP_HTTP" != "no"; then AC_CHECK_PROG(SED, sed, sed) ]) ]) - + AC_PROG_CPP - + if test "$PHP_HTTP_SHARED_DEPS" != "no"; then AC_DEFINE([PHP_HTTP_SHARED_DEPS], [1], [ ]) else AC_DEFINE([PHP_HTTP_SHARED_DEPS], [0], [ ]) fi - + dnl dnl HTTP_SHARED_DEP(name[, code-if-yes[, code-if-not]]) dnl AC_DEFUN([HTTP_SHARED_DEP], [ extname=$1 haveext=$[PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__) - + AC_MSG_CHECKING([whether to add a dependency on ext/$extname]) if test "$PHP_HTTP_SHARED_DEPS" = "no"; then AC_MSG_RESULT([no]) @@ -63,7 +63,7 @@ if test "$PHP_HTTP" != "no"; then $3 fi ]) - + dnl dnl HTTP_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) dnl @@ -92,7 +92,7 @@ if test "$PHP_HTTP" != "no"; then $3 fi ]) - + dnl dnl HTTP_CURL_SSL_LIB_CHECK(ssllib[, code-if-yes[, code-if-not]) dnl @@ -120,7 +120,7 @@ if test "$PHP_HTTP" != "no"; then $3 ]) ]) - + dnl ---- dnl STDC @@ -240,7 +240,7 @@ dnl ---- PHP_ADD_LIBRARY_WITH_PATH(z, $ZLIB_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) fi fi - + dnl ---- dnl CURL dnl ---- @@ -260,7 +260,7 @@ dnl ---- AC_MSG_RESULT([not found]) else AC_MSG_RESULT([found in $CURL_DIR]) - + AC_MSG_CHECKING([for curl-config]) CURL_CONFIG= for i in "$CURL_DIR/bin/curl-config" "$CURL_DIR/curl-config" `which curl-config`; do @@ -275,7 +275,7 @@ dnl ---- else AC_MSG_RESULT([found: $CURL_CONFIG]) fi - + dnl RHEL6: 7.19.7 dnl SUSE11: 7.19.7 dnl Debian wheezy: 7.26.0 @@ -287,7 +287,7 @@ dnl ---- if test `echo $CURL_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*10000 + $2*100 + $3}'` -lt 71802; then AC_MSG_ERROR([libcurl version greater or equal to 7.18.2 required]) fi - + AC_MSG_CHECKING([for HTTP2 support in libcurl]) if $CURL_CONFIG --features | $EGREP -q HTTP2; then AC_MSG_RESULT([yes]) @@ -295,11 +295,11 @@ dnl ---- else AC_MSG_RESULT([no]) fi - + dnl dnl compile tests dnl - + save_INCLUDES="$INCLUDES" INCLUDES= save_LIBS="$LIBS" @@ -308,14 +308,14 @@ dnl ---- CFLAGS="$CFLAGS `$CURL_CONFIG --cflags`" save_LDFLAGS="$LDFLAGS" LDFLAGS="$ld_runpath_switch$CURL_DIR/$PHP_LIBDIR" - + AC_MSG_CHECKING([for SSL support in libcurl]) CURL_SSL=`$CURL_CONFIG --feature | $EGREP SSL` CURL_SSL_LIBS="" if test "$CURL_SSL" = "SSL"; then AC_MSG_RESULT([yes]) AC_DEFINE([PHP_HTTP_HAVE_SSL], [1], [ ]) - + HTTP_CURL_SSL_LIB_CHECK(OpenSSL, [ AC_CHECK_HEADER([openssl/ssl.h], [ AC_CHECK_HEADER([openssl/crypto.h], [ @@ -345,7 +345,7 @@ dnl ---- dnl no CURL_SSL AC_MSG_RESULT([no]) fi - + AC_MSG_CHECKING([for ares support in libcurl]) AC_TRY_RUN([ #include @@ -364,7 +364,7 @@ dnl ---- ], [ AC_MSG_RESULT([no]) ]) - + AC_MSG_CHECKING([whether CURLOPT_TLSAUTH_TYPE expects CURL_TLSAUTH_SRP or literal "SRP"]) AC_TRY_RUN([ #include @@ -391,24 +391,24 @@ dnl ---- AC_MSG_RESULT([neither]) ], [ AC_MSG_RESULT([neither]) - ]) + ]) ], [ AC_MSG_RESULT([neither]) ]) - + INCLUDES="$save_INCLUDES" LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" LDFLAGS="$save_LDFLAGS" - + if test -n "$CURL_SSL_LIBS"; then for CURL_SSL_LIB in $CURL_SSL_LIBS; do PHP_ADD_LIBRARY_WITH_PATH([$CURL_SSL_LIB], $CURL_DIR/$PHP_LIBDIR, PHP_HTTP_SHARED_LIBADD) done fi - + dnl end compile tests - + AC_MSG_CHECKING([for bundled SSL CA info]) CURL_CAINFO= for i in `$CURL_CONFIG --ca` "/etc/ssl/certs/ca-certificates.crt" "/etc/ssl/certs/ca-bundle.crt"; do @@ -423,7 +423,7 @@ dnl ---- AC_MSG_RESULT([$CURL_CAINFO]) AC_DEFINE_UNQUOTED([PHP_HTTP_CURL_CAINFO], ["$CURL_CAINFO"], [path to bundled SSL CA info]) fi - + PHP_ADD_INCLUDE($CURL_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(curl, $CURL_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) PHP_EVAL_LIBLINE(`$CURL_CONFIG --libs`, HTTP_SHARED_LIBADD) @@ -453,9 +453,9 @@ dnl ---- AC_DEFINE([PHP_HTTP_HAVE_EVENT], [0], [ ]) else AC_MSG_RESULT([found in $EVENT_DIR]) - + AC_MSG_CHECKING([for libevent version, roughly]) - + if test -f "$EVENT_DIR/include/event2/event.h"; then EVENT_VER="`$AWK '/_EVENT_VERSION/ {gsub(/\"/,\"\",$3); print $3}' < $EVENT_DIR/include/event2/event-config.h`" AC_DEFINE([PHP_HTTP_HAVE_EVENT2], [1], [ ]) @@ -473,7 +473,7 @@ dnl ---- fi AC_DEFINE_UNQUOTED([PHP_HTTP_EVENT_VERSION], ["$EVENT_VER"], [ ]) AC_MSG_RESULT([$EVENT_VER]) - + PHP_ADD_INCLUDE($EVENT_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(event, $EVENT_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) AC_DEFINE([PHP_HTTP_HAVE_EVENT], [1], [Have libevent support for cURL]) @@ -573,90 +573,59 @@ dnl ---- dnl ---- dnl DONE dnl ---- + PHP_ADD_INCLUDE(src) + PHP_ADD_BUILD_DIR(src) PHP_HTTP_SOURCES="\ - php_http_buffer.c \ - php_http.c \ - php_http_client.c \ - php_http_client_curl.c \ - php_http_client_request.c \ - php_http_client_response.c \ - php_http_cookie.c \ - php_http_curl.c \ - php_http_encoding.c \ - php_http_env.c \ - php_http_env_request.c \ - php_http_env_response.c \ - php_http_etag.c \ - php_http_exception.c \ - php_http_filter.c \ - php_http_header_parser.c \ - php_http_header.c \ - php_http_info.c \ - php_http_message_body.c \ - php_http_message.c \ - php_http_message_parser.c \ - php_http_misc.c \ - php_http_negotiate.c \ - php_http_object.c \ - php_http_options.c \ - php_http_params.c \ - php_http_querystring.c \ - php_http_url.c \ - php_http_version.c \ + src/php_http_buffer.c \ + src/php_http.c \ + src/php_http_client.c \ + src/php_http_client_curl.c \ + src/php_http_client_request.c \ + src/php_http_client_response.c \ + src/php_http_cookie.c \ + src/php_http_curl.c \ + src/php_http_encoding.c \ + src/php_http_env.c \ + src/php_http_env_request.c \ + src/php_http_env_response.c \ + src/php_http_etag.c \ + src/php_http_exception.c \ + src/php_http_filter.c \ + src/php_http_header_parser.c \ + src/php_http_header.c \ + src/php_http_info.c \ + src/php_http_message_body.c \ + src/php_http_message.c \ + src/php_http_message_parser.c \ + src/php_http_misc.c \ + src/php_http_negotiate.c \ + src/php_http_object.c \ + src/php_http_options.c \ + src/php_http_params.c \ + src/php_http_querystring.c \ + src/php_http_url.c \ + src/php_http_version.c \ " PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared) - + dnl shared extension deps HTTP_SHARED_DEP([hash]) HTTP_SHARED_DEP([iconv]) - + 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]) fi diff --git a/gen_curlinfo.php b/gen_curlinfo.php deleted file mode 100755 index 076a10d..0000000 --- a/gen_curlinfo.php +++ /dev/null @@ -1,99 +0,0 @@ -#!/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); - } -', -'DOUBLE' => -' if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) { - add_assoc_double_ex(&array, "%s", sizeof("%2$s"), d); - } -', -'LONG' => -' if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) { - add_assoc_long_ex(&array, "%s", sizeof("%2$s"), l); - } -', -'SLIST' => -' if (CURLE_OK == curl_easy_getinfo(ch, %s, &s)) { - MAKE_STD_ZVAL(subarray); - array_init(subarray); - for (p = s; p; p = p->next) { - if (p->data) { - add_next_index_string(subarray, p->data, 1); - } - } - add_assoc_zval_ex(&array, "%s", sizeof("%2$s"), subarray); - 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 --git a/gen_travis_yml.php b/gen_travis_yml.php deleted file mode 100755 index 89ab0b2..0000000 --- a/gen_travis_yml.php +++ /dev/null @@ -1,45 +0,0 @@ -#!/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"], - "enable_debug", - "enable_maintainer_zts", - "enable_json", - "enable_hash" => ["yes"], - "enable_iconv" => ["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 - -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 --git a/gen_utf8.php b/gen_utf8.php deleted file mode 100755 index 865a2f5..0000000 --- a/gen_utf8.php +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env php -= 2 ? $argv[1] : "/usr/share/i18n/locales/i18n"; - -$f = fopen($i18n, "r"); -$c = false; -$a = false; - -ob_start(null, 0xffff); -while (!feof($f)) { - $line = fgets($f); - if (!$c && $line !== "LC_CTYPE\n") { - continue; - } - $c = true; - if ($line === "END LC_CTYPE\n") { - break; - } - switch($line{0}) { - case "%": - break; - case "\n": - if ($a) { - break 2; - } - break; - case " ": - if ($a) { - foreach (explode(";", trim($line, "\n/ ;")) as $ranges) { - $range = explode("..", $ranges); - $step = 0; - $end = 0; - switch (count($range)) { - case 3: - list($sstart, $sstep, $send) = $range; - sscanf($sstart, "", $start); - sscanf($sstep, "(%d)", $step); - sscanf($send, "", $end); - - break; - case 2: - list($sstart, $send) = $range; - $step = 1; - sscanf($sstart, "", $start); - sscanf($send, "", $end); - break; - case 1: - list($sstart) = $range; - sscanf($sstart, "", $start); - break; - } - print "\t{"; - if ($start >= 0xffff) { - printf("0x%08X, ", $start); - if ($end) { - printf("0x%08X, ", $end); - } else { - print(" 0, "); - } - } else { - printf(" 0x%04X, ", $start); - if ($end) { - printf(" 0x%04X, ", $end); - } else { - print(" 0, "); - } - } - printf("%d},\n", $step); - } - } - break; - default: - if ($a) { - break 2; - } elseif ($line === "alpha /\n") { - $a = true; - } - break; - } -} - -file_put_contents("php_http_utf8.h", - preg_replace('/(\/\* BEGIN::UTF8TABLE \*\/\n).*(\n\s*\/\* END::UTF8TABLE \*\/)/s', '$1'. ob_get_contents() .'$2', - file_get_contents("php_http_utf8.h"))); diff --git a/package.xml b/package.xml index df97ad9..95578ea 100644 --- a/package.xml +++ b/package.xml @@ -1,4 +1,4 @@ - + pecl.php.net Extended HTTP Support stable stable - BSD, revised + BSD-2-Clause - + + + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -329,6 +333,14 @@ http://dev.iworks.at/ext-http/lcov/ext/http/ + + + + + + + + @@ -336,7 +348,7 @@ http://dev.iworks.at/ext-http/lcov/ext/http/ 5.3.0 7.0.0 - 7.0.0 + 7.0.0 1.4.1 @@ -345,12 +357,16 @@ http://dev.iworks.at/ext-http/lcov/ext/http/ raphf pecl.php.net 1.1.0 + 2.0.0dev + 2.0.0dev raphf propro pecl.php.net 1.0.0 + 2.0.0dev + 2.0.0dev propro diff --git a/php_http.c b/php_http.c deleted file mode 100644 index 2ff20f1..0000000 --- a/php_http.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_free(zend_php_http_globals *G TSRMLS_DC) -{ -} -#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_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 --git a/php_http_api.h b/php_http_api.h deleted file mode 100644 index 08b6ba8..0000000 --- a/php_http_api.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; -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)]) -# undef TSRMLS_FETCH_FROM_CTX -# define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = ((ctx)?(ctx):ts_resource_ex(0, NULL)) -#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 --git a/php_http_buffer.c b/php_http_buffer.c deleted file mode 100644 index e24a4e1..0000000 --- a/php_http_buffer.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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->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) -{ - if ((buf = php_http_buffer_init(buf))) { - if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) { - 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) -{ - 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); - } 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) -{ - 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) -{ - 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, ...) -{ - 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) -{ - 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) -{ - 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); - 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) -{ - 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_t *storage; - - *chunk = NULL; - - if (!*s) { - *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) -{ - 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)) { - 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) -{ - size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC); - - 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); - - 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_t *str; - size_t passed; - - if (!*s) { - *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); - - 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) -{ - 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) -{ - 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) -{ - 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, ...) -{ - 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) -{ - 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, ...) -{ - 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) -{ - 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)); - 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) -{ - 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) -{ - 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, ...) -{ - 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 --git a/php_http_buffer.h b/php_http_buffer.h deleted file mode 100644 index faf8992..0000000 --- a/php_http_buffer.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; \ - 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); - -/* 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 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)) -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); - -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); - -/* 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); - -/* 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); - - -# 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 --git a/php_http_client.c b/php_http_client.c deleted file mode 100644 index 160e8bb..0000000 --- a/php_http_client.c +++ /dev/null @@ -1,1284 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - -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); -} - -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 *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; - } - return FAILURE; -} - -static int apply_driver_list(void *p, void *arg TSRMLS_DC) -{ - php_http_client_driver_t *d = p; - zval *zname; - - MAKE_STD_ZVAL(zname); - ZVAL_STRINGL(zname, d->name_str, d->name_len, 1); - - zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL); - return ZEND_HASH_APPLY_KEEP; -} - -void php_http_client_driver_list(HashTable *ht TSRMLS_DC) -{ - zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC); -} - -void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC) -{ - 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; - - MAKE_STD_ZVAL(new_opts); - array_init(new_opts); - old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); - if (Z_TYPE_P(old_opts) == IS_ARRAY) { - array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(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); - } else { - zend_symtable_del(Z_ARRVAL_P(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); - } else { - Z_ADDREF_P(opts); - zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL); - } - } - - zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC); - zval_ptr_dtor(&new_opts); - } -} - -void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC) -{ - 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); - - MAKE_STD_ZVAL(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); - zval_ptr_dtor(&new_opts); - } else { - zval *old_opts, *add_opts, **opt; - - MAKE_STD_ZVAL(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); - if (Z_TYPE_P(old_opts) == IS_ARRAY) { - zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len); - } - } else { - Z_ADDREF_P(*opt); - add_assoc_zval_ex(add_opts, key.str, key.len, *opt); - } - } - } - - old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); - if (Z_TYPE_P(old_opts) == IS_ARRAY) { - array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(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); - 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) -{ - zend_class_entry *this_ce = Z_OBJCE_P(getThis()); - zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); - - if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) { - 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 *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); - } - } - } - - 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"); - 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"); - 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) -{ - php_http_client_object_t *o = (php_http_client_object_t *) object; - - 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_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 *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->client = client; - - if (ptr) { - *ptr = o; - } - - 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; -} - -zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC) -{ - return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response TSRMLS_DC) -{ - 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); - - MAKE_STD_ZVAL(new_hist); - ZVAL_OBJVAL(new_hist, ov, 0); - - if (Z_TYPE_P(old_hist) == IS_OBJECT) { - php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC); - } - - zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC); - 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); - - if ((msg = *response)) { - php_http_message_object_t *msg_obj; - zval *info, *zresponse, *zrequest; - 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); - } - - /* 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); - - php_http_message_object_prepend(zresponse, zrequest, 1 TSRMLS_CC); - - MAKE_STD_ZVAL(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); - zval_ptr_dtor(&info); - - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); - zend_llist_add_element(&client->responses, &msg_obj); - - if (e->closure.fci.size) { - zval *retval = NULL; - 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); - - if (retval) { - if (Z_TYPE_P(retval) == IS_BOOL) { - dequeue = Z_BVAL_P(retval); - } - 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]; - 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_ptr_dtor(&zclient); - zval_ptr_dtor(&zrequest); - zval_ptr_dtor(&zprogress); -} - -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_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; - php_resource_factory_t *rf = NULL; - php_http_client_object_t *obj; - 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); - - 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); - 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); - zval_ptr_dtor(&os); - - if (persistent_handle_len) { - char *name_str; - size_t name_len; - 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))) { - rf = php_persistent_handle_resource_factory_init(NULL, pf); - } - - efree(name_str); - } - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return); - - php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC); - - 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->iterator = 0; - php_http_client_reset(obj->client); - - RETVAL_ZVAL(getThis(), 1, 0); -} - -static HashTable *combined_options(zval *client, zval *request TSRMLS_DC) -{ - 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); - - 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)); - 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); - } - 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_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); - } - } -} - -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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); - - 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.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); - } - } - - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - msg_obj = zend_object_store_get_object(request TSRMLS_CC); - - 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.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); - } - } - - zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); - - 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; - - 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); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - 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); - } - } - - /* 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); - 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; - - 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); - 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); - - 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); - - 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)) { - struct timeval timeout_val; - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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 == 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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]; - int argc; -}; - -static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC) -{ - zval **observer = NULL; - 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); - } - return FAILURE; -} - -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; - 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); - - 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); - - 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(); - arg.argc = 1; - - if (request) { - Z_ADDREF_P(request); - arg.args[1] = &request; - arg.argc += 1; - } - - if (zprogress) { - Z_ADDREF_P(zprogress); - arg.args[2] = &zprogress; - arg.argc += 1; - } - - spl_iterator_apply(observers, notify, &arg TSRMLS_CC); - - zval_ptr_dtor(&getThis()); - if (request) { - zval_ptr_dtor(&request); - } - if (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; - 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); - - 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); - - 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); - } - - zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer); - if (retval) { - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return); - - observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); - - 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); - } - - 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; - - 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); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - req_obj = zend_object_store_get_object(request TSRMLS_CC); - - 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_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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - req_obj = zend_object_store_get_object(request TSRMLS_CC); - - 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_client_options_set(getThis(), opts TSRMLS_CC); - - 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); - 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); - - 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); - - 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); - } -} - -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_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC); - - 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_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC); - - 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); - } -} - -ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers, 0, 0, 0) -ZEND_END_ARG_INFO(); -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); - } -} - -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); - - 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); - - 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->create_object = php_http_client_object_new; - zend_class_implements(php_http_client_class_entry TSRMLS_CC, 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.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_hash_init(&php_http_client_drivers, 2, NULL, NULL, 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 --git a/php_http_client.h b/php_http_client.h deleted file mode 100644 index f4a5b59..0000000 --- a/php_http_client.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - 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); - -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; -} 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_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 --git a/php_http_client_curl.c b/php_http_client_curl.c deleted file mode 100644 index fed92fc..0000000 --- a/php_http_client_curl.c +++ /dev/null @@ -1,2599 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - - } 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) -{ - 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) -{ - 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) -{ - 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) -{ - return curl_multi_init(); -} - -static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC) -{ - 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) { - 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); - - /* 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); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) { - add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) { - add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) { - add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) { - add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) { - add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) { - add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) { - add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) { - add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) { - add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) { - add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) { - add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) { - add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) { - add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l); - } - 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); - } - 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); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) { - add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) { - add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) { - add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) { - add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) { - add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) { - add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) { - add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) { - add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) { - add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l); - } - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) { - MAKE_STD_ZVAL(subarray); - array_init(subarray); - for (p = s; p; p = p->next) { - if (p->data) { - add_next_index_string(subarray, p->data, 1); - } - } - add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray); - 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); - } -#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); - } -#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); - } -#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); - } -#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); - } -#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); - } -#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); - } -#endif - - /* END::CURLINFO */ - -#if PHP_HTTP_CURL_VERSION(7,34,0) - { - zval *ti_array; - struct curl_tlssessioninfo *ti; - - if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) { - const char *backend; - - MAKE_STD_ZVAL(subarray); - ZVAL_NULL(subarray); - MAKE_STD_ZVAL(ti_array); - 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)); - } -#endif - break; - case CURLSSLBACKEND_GNUTLS: - backend = "gnutls"; -#ifdef PHP_HTTP_HAVE_GNUTLS - { - gnutls_session_t sess = ti->internals; - char *desc; - - array_init(subarray); - if ((desc = gnutls_session_get_desc(sess))) { - add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1); - gnutls_free(desc); - } - add_assoc_bool_ex(subarray, ZEND_STRS("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); - } - } -#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; - 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); - - for (i = 0; i < ci->num_of_certs; ++i) { - s = ci->certinfo[i]; - - MAKE_STD_ZVAL(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); - efree(keyname); - } else { - add_next_index_string(subarray, p->data, 1); - } - } - } - add_next_index_zval(ci_array, subarray); - } - add_assoc_zval_ex(&array, "certinfo", sizeof("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); - } - - 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) -{ - 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); - 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("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("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-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")); - } - 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); - - 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)); - 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_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_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); - 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)) { - 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)) { - 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) { - 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)) { - 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; - - FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) { - zval *zv = php_http_ztyp(IS_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); - - zval_ptr_dtor(&zv); - } - - 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); - 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))) { - 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)) { - 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)) { - 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_ptr_dtor(&rbl); - zval_ptr_dtor(&rel); - } - - } - } - } - - 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; - - switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) { - 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; - /* 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); - } - 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_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)); - 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); - } - } - 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; - - 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); - } - - 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) -{ - 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); -#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); - - /* 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); -#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))) { - 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, - "PECL_HTTP/" PHP_PECL_HTTP_VERSION " " - "PHP/" PHP_VERSION " " - "libcurl/" LIBCURL_VERSION - , 0); - } - - /* 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))) { - 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))) { - 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); - /* 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); -#if PHP_HTTP_CURL_VERSION(7,25,0) - 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); - } - 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); - } - 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))) { - ZVAL_BOOL(&opt->defval, 1); - } - 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); -#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); -#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); -#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))) { - ZVAL_BOOL(&opt->defval, 1); - } - 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); -#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); - } - 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: - if (opt->setter) { - rv = opt->setter(opt, val, curl); - } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) { - 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 ((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)) { - 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)); - } - 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; - 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); - } - } - - 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)); -} -#endif - -static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC) -{ - 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); - /* 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))) { - 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); - } - - 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)))) { - 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); - } - - if (rv != SUCCESS) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, 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"); - 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); - 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"); - 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_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); -#endif - - php_http_buffer_init(&header); - FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) { - if (header_key.type == HASH_KEY_IS_STRING) { -#if !PHP_HTTP_CURL_VERSION(7,23,0) - /* avoid duplicate content-length header */ - 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)); - 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); - } - } - 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"); - 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_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 (handler->options.headers) { - curl_slist_free_all(handler->options.headers); - handler->options.headers = 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"); - 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); - - 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) -{ - 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"); - return NULL; - } - - /* only if the client itself is setup for persistence */ - if (php_resource_factory_is_persistent(h->rf)) { - char *id_str = NULL; - size_t id_len; - int port = url->port ? url->port : 80; - zval **zport; - - if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) { - zval *zcpy = php_http_ztyp(IS_LONG, *zport); - - if (Z_LVAL_P(zcpy)) { - port = Z_LVAL_P(zcpy); - } - 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); - } - - 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); - 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)); - 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)); - } - - 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()"); - 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()); -#else - php_error_docref(NULL TSRMLS_CC, 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) -{ - php_http_option_t *opt = pDest; - HashTable *ht; - 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)); - } else { - /* catch deliberate NULL options */ - if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) { - ZVAL_NULL(entry); - } else { - ZVAL_COPY_VALUE(entry, &opt->defval); - zval_copy_ctor(entry); - } - } - - if (hash_key->nKeyLength) { - zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL); - } else { - zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL); - } - - 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); - 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); - 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)) { - return FAILURE; - } - - if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) { - return FAILURE; - } - if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) { - 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); - } - 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); - } - - /* - * 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_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 --git a/php_http_client_curl.h b/php_http_client_curl.h deleted file mode 100644 index c82a09c..0000000 --- a/php_http_client_curl.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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 - -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 --git a/php_http_client_request.c b/php_http_client_request.c deleted file mode 100644 index 0e40cc5..0000000 --- a/php_http_client_request.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - -#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); \ - } \ - } 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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - } - - if (zbody) { - php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody TSRMLS_CC), 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); - } - 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; - php_http_message_object_t *obj; - zval *zct; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ct_str, &ct_len), 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); - return; - } - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - 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); - 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); - if (zct) { - RETURN_ZVAL(zct, 0, 1); - } - } -} - -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; - 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_CLIENT_REQUEST_OBJECT_INIT(obj); - - 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, - 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); - - if (old_url) { - php_http_url_free(&old_url); - } - if (new_url.query != &empty[0]) { - PTR_FREE(new_url.query); - } - - 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_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); - } - } -} - -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_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, - 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); - - if (old_url) { - php_http_url_free(&old_url); - } - PTR_FREE(new_url.query); - - 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_client_options_set(getThis(), opts TSRMLS_CC); - - 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); - 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); - - 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); - - 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); - } -} - -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); - - zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC); - - 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 --git a/php_http_client_request.h b/php_http_client_request.h deleted file mode 100644 index 474114b..0000000 --- a/php_http_client_request.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_REQUEST_H -#define PHP_HTTP_CLIENT_REQUEST_H - -PHP_HTTP_API zend_class_entry *php_http_client_request_class_entry; -PHP_MINIT_FUNCTION(http_client_request); - -#endif /* PHP_HTTP_CLIENT_REQUEST_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 --git a/php_http_client_response.c b/php_http_client_response.c deleted file mode 100644 index 8d512ec..0000000 --- a/php_http_client_response.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - zval *allowed_extras_array = NULL; - int i = 0; - char **allowed_extras = NULL; - zval *header = NULL, **entry = NULL; - HashPosition pos; - php_http_message_object_t *msg; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) { - return; - } - - msg = zend_object_store_get_object(getThis() TSRMLS_CC); - array_init(return_value); - - if (allowed_extras_array) { - 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); - } - } - - if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie"), 0))) { - php_http_cookie_list_t *list; - - if (Z_TYPE_P(header) == IS_ARRAY) { - zval **single_header; - - FOREACH_VAL(pos, header, single_header) { - zval *data = php_http_ztyp(IS_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; - - 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_ptr_dtor(&data); - } - } 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; - - 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_ptr_dtor(&data); - } - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); - - /* 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); - - 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); - - 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 --git a/php_http_client_response.h b/php_http_client_response.h deleted file mode 100644 index 15d004e..0000000 --- a/php_http_client_response.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_RESPONSE_H -#define PHP_HTTP_CLIENT_RESPONSE_H - -PHP_HTTP_API zend_class_entry *php_http_client_response_class_entry; -PHP_MINIT_FUNCTION(http_client_response); - -#endif /* PHP_HTTP_CLIENT_RESPONSE_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 --git a/php_http_cookie.c b/php_http_cookie.c deleted file mode 100644 index 354dfa6..0000000 --- a/php_http_cookie.c +++ /dev/null @@ -1,1038 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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); - - 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) -{ - zval **cookie; - if ((SUCCESS != zend_symtable_find(&list->cookies, name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) { - return NULL; - } - if (zcookie) { - *zcookie = *cookie; - } - return Z_STRVAL_PP(cookie); -} - -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; - - if ((SUCCESS != zend_symtable_find(&list->extras, name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) { - return NULL; - } - if (zextra) { - *zextra = *extra; - } - return Z_STRVAL_PP(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; - - 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); -} - -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; - - 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); -} - -#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) -{ - zval *arg = php_http_zsep(1, IS_STRING, val); - - if (!(flags & PHP_HTTP_COOKIE_PARSE_RAW)) { - Z_STRLEN_P(arg) = php_raw_url_decode(Z_STRVAL_P(arg), Z_STRLEN_P(arg)); - } - - if _KEY_IS("path") { - PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); - } else if _KEY_IS("domain") { - PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); - } else if _KEY_IS("expires") { - char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(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); - } else if _KEY_IS("secure") { - list->flags |= PHP_HTTP_COOKIE_SECURE; - } else if _KEY_IS("httpOnly") { - list->flags |= PHP_HTTP_COOKIE_HTTPONLY; - } else { - /* 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); - 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); - } - 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_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; - - 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); - 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); - } - 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); - } - } - } - } - 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)); - - 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); - - 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); - - 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); -} - -php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC) -{ - 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 *)); - } - 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 (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 (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) { - if (Z_TYPE_PP(tmp) == IS_LONG) { - list->expires = Z_LVAL_PP(tmp); - } else { - long lval; - - cpy = php_http_ztyp(IS_STRING, *tmp); - if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { - list->expires = lval; - } else { - list->expires = php_parse_date(Z_STRVAL_P(cpy), NULL); - } - - zval_ptr_dtor(&cpy); - } - } - 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); - } else { - long lval; - - cpy = php_http_ztyp(IS_STRING, *tmp); - if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { - list->max_age = lval; - } - - zval_ptr_dtor(&cpy); - } - } - 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 (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)); - } - - 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]; - - 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]); - - php_http_buffer_append(buf, enc_str[0], enc_len[0]); - php_http_buffer_appends(buf, "="); - php_http_buffer_append(buf, enc_str[1], enc_len[1]); - php_http_buffer_appends(buf, "; "); - - efree(enc_str[0]); - efree(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); - - 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); - - zval_ptr_dtor(&tmp); - } - - 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); - } - 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); - } - - 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) -{ - return php_http_cookie_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *o; - - o = ecalloc(sizeof(*o), 1); - zend_object_std_init((zend_object *) o, ce TSRMLS_CC); - object_properties_init((zend_object *) o, ce); - - 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; -} - -#define PHP_HTTP_COOKIE_OBJECT_INIT(obj) \ - do { \ - if (!obj->list) { \ - obj->list = php_http_cookie_list_init(NULL TSRMLS_CC); \ - } \ - } while(0) - -zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC) -{ - 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_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); - - return ov; -} - -void php_http_cookie_object_free(void *object TSRMLS_DC) -{ - php_http_cookie_object_t *obj = object; - - php_http_cookie_list_free(&obj->list); - zend_object_std_dtor((zend_object *) obj TSRMLS_CC); - efree(obj); -} - -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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); - 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; - - ae = ae_ptr; - FOREACH_HASH_VAL(pos, allowed_extras, val) { - zval *cpy = php_http_ztyp(IS_STRING, *val); - - *ae_ptr++ = estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)); - zval_ptr_dtor(&cpy); - } - *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 (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); - break; - default: { - zval *cpy = php_http_ztyp(IS_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); - 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); - - 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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - array_init(return_value); - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); -} - -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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - array_init(return_value); - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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; - php_http_cookie_object_t *obj; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { - return; - } - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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; - php_http_cookie_object_t *obj; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { - return; - } - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - if (obj->list->domain) { - RETURN_STRING(obj->list->domain, 1); - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - if (obj->list->path) { - RETURN_STRING(obj->list->path, 1); - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - 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; - php_http_cookie_object_t *obj; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - obj->list->max_age = ts; - - 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); - - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - php_http_cookie_list_to_string(obj->list, &str, &len); - - RETURN_STRINGL(str, len, 0); -} - -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); - - PHP_HTTP_COOKIE_OBJECT_INIT(obj); - - array_init(return_value); - 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->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.clone_obj = php_http_cookie_object_clone; - - 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); - - 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 --git a/php_http_cookie.h b/php_http_cookie.h deleted file mode 100644 index 7cf00fe..0000000 --- a/php_http_cookie.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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) -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); - -#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) -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 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 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; -} 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); - -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 --git a/php_http_curl.c b/php_http_curl.c deleted file mode 100644 index a995094..0000000 --- a/php_http_curl.c +++ /dev/null @@ -1,146 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_CURL - -#if defined(ZTS) && defined(PHP_HTTP_HAVE_SSL) -# ifdef PHP_WIN32 -# define PHP_HTTP_NEED_OPENSSL_TSL -# include -# else /* !PHP_WIN32 */ -# if defined(PHP_HTTP_HAVE_OPENSSL) -# define PHP_HTTP_NEED_OPENSSL_TSL -# include -# elif defined(PHP_HTTP_HAVE_GNUTLS) -# define PHP_HTTP_NEED_GNUTLS_TSL -# include -# endif /* PHP_HTTP_HAVE_OPENSSL || PHP_HTTP_HAVE_GNUTLS */ -# endif /* PHP_WIN32 */ -#endif /* ZTS && PHP_HTTP_HAVE_SSL */ - - -#ifdef PHP_HTTP_NEED_OPENSSL_TSL -static MUTEX_T *php_http_openssl_tsl = NULL; - -static void php_http_openssl_thread_lock(int mode, int n, const char * file, int line) -{ - if (mode & CRYPTO_LOCK) { - tsrm_mutex_lock(php_http_openssl_tsl[n]); - } else { - tsrm_mutex_unlock(php_http_openssl_tsl[n]); - } -} - -static ulong php_http_openssl_thread_id(void) -{ - return (ulong) tsrm_thread_id(); -} -#endif -#ifdef PHP_HTTP_NEED_GNUTLS_TSL -static int php_http_gnutls_mutex_create(void **m) -{ - if (*((MUTEX_T *) m) = tsrm_mutex_alloc()) { - return SUCCESS; - } else { - return FAILURE; - } -} - -static int php_http_gnutls_mutex_destroy(void **m) -{ - tsrm_mutex_free(*((MUTEX_T *) m)); - return SUCCESS; -} - -static int php_http_gnutls_mutex_lock(void **m) -{ - return tsrm_mutex_lock(*((MUTEX_T *) m)); -} - -static int php_http_gnutls_mutex_unlock(void **m) -{ - return tsrm_mutex_unlock(*((MUTEX_T *) m)); -} - -static struct gcry_thread_cbs php_http_gnutls_tsl = { - GCRY_THREAD_OPTION_USER, - NULL, - php_http_gnutls_mutex_create, - php_http_gnutls_mutex_destroy, - php_http_gnutls_mutex_lock, - php_http_gnutls_mutex_unlock -}; -#endif - - -PHP_MINIT_FUNCTION(http_curl) -{ -#ifdef PHP_HTTP_NEED_OPENSSL_TSL - /* mod_ssl, libpq or ext/curl might already have set thread lock callbacks */ - if (!CRYPTO_get_id_callback()) { - int i, c = CRYPTO_num_locks(); - - php_http_openssl_tsl = malloc(c * sizeof(MUTEX_T)); - - for (i = 0; i < c; ++i) { - php_http_openssl_tsl[i] = tsrm_mutex_alloc(); - } - - CRYPTO_set_id_callback(php_http_openssl_thread_id); - CRYPTO_set_locking_callback(php_http_openssl_thread_lock); - } -#endif -#ifdef PHP_HTTP_NEED_GNUTLS_TSL - gcry_control(GCRYCTL_SET_THREAD_CBS, &php_http_gnutls_tsl); -#endif - - if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) { - return FAILURE; - } - - return SUCCESS; -} - -PHP_MSHUTDOWN_FUNCTION(http_curl) -{ - curl_global_cleanup(); -#ifdef PHP_HTTP_NEED_OPENSSL_TSL - if (php_http_openssl_tsl) { - int i, c = CRYPTO_num_locks(); - - CRYPTO_set_id_callback(NULL); - CRYPTO_set_locking_callback(NULL); - - for (i = 0; i < c; ++i) { - tsrm_mutex_free(php_http_openssl_tsl[i]); - } - - free(php_http_openssl_tsl); - php_http_openssl_tsl = NULL; - } -#endif - 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 --git a/php_http_curl.h b/php_http_curl.h deleted file mode 100644 index ab8b63c..0000000 --- a/php_http_curl.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_CURL_H -#define PHP_HTTP_CURL_H - -#if PHP_HTTP_HAVE_CURL - -#include -#define PHP_HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z))) - -PHP_MINIT_FUNCTION(http_curl); -PHP_MSHUTDOWN_FUNCTION(http_curl); - -#endif /* PHP_HTTP_HAVE_CURL */ - -#endif /* PHP_HTTP_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 --git a/php_http_encoding.c b/php_http_encoding.c deleted file mode 100644 index 286f2b5..0000000 --- a/php_http_encoding.c +++ /dev/null @@ -1,1227 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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"); - 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); - 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)); - } 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); - } - } - 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); - 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) -{ - 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)); - return FAILURE; -} - -ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC) -{ - 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)); - 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) -{ - 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)); - 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)); - 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)); - 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)); - 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"); - 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)); - 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)); - 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"); - 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_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)); - 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)); - 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)); - 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) -{ - return php_http_encoding_stream_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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; - } - - 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; - - return o->zv; -} - -zend_object_value php_http_encoding_stream_object_clone(zval *this_ptr TSRMLS_DC) -{ - zend_object_value new_ov; - php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - return new_ov; -} - -void php_http_encoding_stream_object_free(void *object TSRMLS_DC) -{ - php_http_encoding_stream_object_t *o = (php_http_encoding_stream_object_t *) object; - - if (o->stream) { - php_http_encoding_stream_free(&o->stream); - } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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)) { - ops = &php_http_encoding_deflate_ops; - } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry TSRMLS_CC)) { - ops = &php_http_encoding_inflate_ops; - } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry TSRMLS_CC)) { - ops = &php_http_encoding_dechunk_ops; - } else { - php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name); - return; - } - - php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags TSRMLS_CC), 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; - 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 (obj->stream) { - 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); - } - } - } -} - -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); - - if (obj->stream) { - char *encoded_str; - 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); - } 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); - - 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); - - if (obj->stream) { - char *encoded_str; - 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); - } 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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags)) { - char *enc_str; - 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); - } - } - 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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { - char *enc_str; - 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); - } - } - 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; - zval *zlen = NULL; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) { - const char *end_ptr; - char *enc_str; - size_t enc_len; - - if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len TSRMLS_CC))) { - if (zlen) { - zval_dtor(zlen); - ZVAL_LONG(zlen, str + len - end_ptr); - } - RETURN_STRINGL(enc_str, enc_len, 0); - } - } - 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->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.clone_obj = php_http_encoding_stream_object_clone; - - 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); - - 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); - - 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); - - 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); - - 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 --git a/php_http_encoding.h b/php_http_encoding.h deleted file mode 100644 index cdf7a1a..0000000 --- a/php_http_encoding.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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); - -typedef struct php_http_encoding_stream_object { - zend_object zo; - zend_object_value zv; - php_http_encoding_stream_t *stream; -} 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); - -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 --git a/php_http_env.c b/php_http_env.c deleted file mode 100644 index c60d4c0..0000000 --- a/php_http_env.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - PHP_HTTP_G->env.server_var = NULL; - } - - return SUCCESS; -} - -void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC) -{ - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **hsv, **header; - HashPosition pos; - - 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); - - 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); - - 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); - - 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); - - 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); - - efree(key.str); - } - } - } - } - - if (headers) { - zend_hash_copy(headers, PHP_HTTP_G->env.request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - } -} - -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) -{ - HashTable *request_headers; - 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); - 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); - - val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); - if (len) { - *len = Z_STRLEN_P(zcopy); - } - zval_ptr_dtor(&zcopy); - } - - 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) -{ - HashTable *request_headers; - char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); - int got; - - if (request) { - request_headers = &request->hdrs; - } else { - php_http_env_get_request_headers(NULL TSRMLS_CC); - request_headers = PHP_HTTP_G->env.request.headers; - } - got = zend_symtable_exists(request_headers, key, name_len + 1); - efree(key); - - return got; -} - -zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC) -{ - zval **hsv; - - zend_is_auto_global(key, key_len TSRMLS_CC); - - if ((SUCCESS != zend_hash_find(&EG(symbol_table), key, key_len + 1, (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) { - return NULL; - } - - return *hsv; -} - -zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check TSRMLS_DC) -{ - zval *hsv, **var; - char *env; - - /* 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)) { - 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))) { - return NULL; - } - if ((SUCCESS != zend_symtable_find(Z_ARRVAL_P(hsv), key, key_len + 1, (void *) &var))) { - return NULL; - } - if (check && !((Z_TYPE_PP(var) == IS_STRING) && Z_STRVAL_PP(var) && Z_STRLEN_PP(var))) { - return NULL; - } - return *var; -} - -php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D) -{ - 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); - } - - return PHP_HTTP_G->env.request.body; -} - -const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC) -{ - 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) -{ - 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))) { - 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); - - 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) -{ - 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) -{ - 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); - php_http_buffer_fix(&headers); - - status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL TSRMLS_CC); - 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 *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); - - if (SUCCESS == zend_symtable_find(&headers, key, name_len + 1, (void *) &zvalue)) { - zval *zcopy = php_http_ztyp(IS_STRING, *zvalue); - - val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); - zval_ptr_dtor(&zcopy); - } - - efree(key); - } - zend_hash_destroy(&headers); - - return val; -} - -long php_http_env_get_response_code(TSRMLS_D) -{ - 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) -{ - return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) http_code TSRMLS_CC); -} - -ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v TSRMLS_DC) -{ - 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); - efree(h.line); - - return ret; -} - -ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC) -{ - return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C), v TSRMLS_CC); -} - -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) -{ - 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); - 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 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); - } - 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 ret; - va_list args; - - va_start(args, fmt); - ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC); - 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) -{ - if (!value) { - sapi_header_line h = {(char *) name_str, name_len, http_code}; - - return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h TSRMLS_CC); - } - - if(Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) { - HashPosition pos; - int first = replace; - zval **data_ptr; - - 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)) { - return FAILURE; - } - first = 0; - } - - return SUCCESS; - } else { - zval *data = php_http_ztyp(IS_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); - } else { - sapi_header_line h; - ZEND_RESULT_CODE ret; - - if (name_len > INT_MAX) { - name_len = INT_MAX; - } - 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)); - - ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC); - - zval_ptr_dtor(&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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); - - if (header_value) { - RETURN_STRINGL(header_value, header_length, 0); - } - } else { - array_init(return_value); - php_http_env_get_request_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); - } -} - -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; - 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); - - 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)) { - php_http_message_body_addref(body); - RETVAL_OBJVAL(ov, 0); - } -} - -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; - const char *status; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { - return; - } - - if ((status = php_http_env_get_response_status_for_code(code))) { - RETURN_STRING(status, 1); - } -} - -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); -#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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); - - if (header_value) { - RETURN_STRING(header_value, 0); - } - } else { - array_init(return_value); - php_http_env_get_response_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); - } -} - -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)); -} - -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; - zval *header_value = NULL; - 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)) { - return; - } - RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header TSRMLS_CC)); -} - -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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { - return; - } - RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code TSRMLS_CC)); -} - -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)) { - return; - } - if (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)) { - return; - } - if (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)) { - return; - } - if (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)) { - return; - } - if (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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) { - return; - } - - - if (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))) { - 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); - - 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 --git a/php_http_env.h b/php_http_env.h deleted file mode 100644 index 3fc80ab..0000000 --- a/php_http_env.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - -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 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_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 --git a/php_http_env_request.c b/php_http_env_request.c deleted file mode 100644 index a884d2f..0000000 --- a/php_http_env_request.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - zval *zfiles, **name, **zname, **error, **zerror, **type, **ztype, **size, **zsize, **tmp_name = zpp; - 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) - ) { - 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); - } else { - 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); - } - } - - return ZEND_HASH_APPLY_KEEP; -} - -static int grab_files(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) -{ - zval *zfiles, **name, **tmp_name, **error, **type, **size, **val = zpp; - - 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) - ) { - 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)) - ) { - zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp_name) TSRMLS_CC, 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); - } - if (key->nKeyLength > 0) { - zend_hash_quick_update(Z_ARRVAL_P(zfiles), key->arKey, key->nKeyLength, key->h, (void *) &cpy, sizeof(zval *), NULL); - } else { - zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, (void *) &cpy, sizeof(zval *), NULL); - } - } - } - - 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); \ - } \ - } 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; - - php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - obj->body = NULL; - - php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST TSRMLS_CC), 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); - 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); - 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); - 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); - } - zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), zqs TSRMLS_CC); - 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); \ - \ - INIT_PZVAL(&mn); \ - array_init(&mn); \ - Z_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); \ - 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_args_clear(&fci, 1); \ - efree(args); \ - zval_dtor(&mn); \ - if (rv) { \ - 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); - 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); - 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); - 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); - 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); - - 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); - - 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 --git a/php_http_env_request.h b/php_http_env_request.h deleted file mode 100644 index a97836b..0000000 --- a/php_http_env_request.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_REQUEST_H -#define PHP_HTTP_ENV_REQUEST_H - -PHP_HTTP_API zend_class_entry *php_http_env_request_class_entry; -PHP_MINIT_FUNCTION(http_env_request); - -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/php_http_env_response.c b/php_http_env_response.c deleted file mode 100644 index 20a31eb..0000000 --- a/php_http_env_response.c +++ /dev/null @@ -1,1513 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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); - break; - case IS_LONG: - zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC); - break; - case IS_STRING: - zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC); - break; - case IS_ARRAY: - case IS_OBJECT: - zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC); - break; - } - } else { - zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC); - } - } 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); - break; - case IS_LONG: - add_assoc_long_ex(options, name_str, name_len + 1, *(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); - break; - case IS_ARRAY: - case IS_OBJECT: - Z_ADDREF_P(value_ptr); - add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr); - break; - } - } - } else { - add_assoc_null_ex(options, name_str, name_len + 1); - } - } -} -static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC) -{ - zval *val, **valptr; - - if (Z_TYPE_P(options) == IS_OBJECT) { - val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC); - } else { - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) { - val = *valptr; - } else { - val = NULL; - } - } - if (val) { - Z_ADDREF_P(val); - } - return val; -} -static php_http_message_body_t *get_body(zval *options TSRMLS_DC) -{ - zval *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); - - body = body_obj->body; - } - zval_ptr_dtor(&zbody); - } - - return body; -} -static php_http_message_t *get_request(zval *options TSRMLS_DC) -{ - zval *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); - - request = request_obj->message; - } - zval_ptr_dtor(&zrequest); - } - - return request; -} -static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC) -{ - 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); - } - - FOREACH_HASH_KEY(pos, &obj->list->cookies, 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); - } else { - add_index_zval(zcookies_set, key.num, zcookie_new); - } - } - - set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC); - zval_ptr_dtor(&zcookies_set); -} - -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 ret = PHP_HTTP_CACHE_NO; - int free_etag = 0; - char *header = NULL, *etag; - php_http_message_body_t *body; - zval *zetag; - - - if (!(body = get_body(options TSRMLS_CC))) { - 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 && 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 (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); - } - - 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 ret = PHP_HTTP_CACHE_NO; - char *header; - time_t ums, lm = 0; - php_http_message_body_t *body; - zval *zlm; - - if (!(body = get_body(options TSRMLS_CC))) { - 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 && Z_LVAL_P(zlm) > 0) { - lm = Z_LVAL_P(zlm); - } else { - 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); - } - - if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) { - 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)) { - return 0; - } - - if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) { - return 0; - } - - return 1; -} - -static size_t output(void *context, char *buf, size_t len TSRMLS_DC) -{ - 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); - PTR_FREE(enc_str); - } else { - chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC); - } - - 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) -{ - 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); - - 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); - - if (r->done) { - return ret; - } - - if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) { - 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); - } - 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); - - 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_copy); - } - - if (ret != SUCCESS) { - return ret; - } - - if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) { - php_http_version_t v; - zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); - - zval_ptr_dtor(&zoption); - if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) { - ret = r->ops->set_protocol_version(r, &v); - php_http_version_dtor(&v); - } - zval_ptr_dtor(&zoption_copy); - } - - if (ret != SUCCESS) { - return ret; - } - - if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) { - if (Z_TYPE_P(zoption) == IS_ARRAY) { - HashPosition pos; - 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); - 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); - } - } - } - 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); - - 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_copy); - } - - 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; - - 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 (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); - } - } 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); - 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); - - 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_copy); - } - - 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; - - 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_dtor(&buf); - zval_ptr_dtor(&zoption_copy); - 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); - zval zsupported; - HashTable *result = NULL; - - zval_ptr_dtor(&zoption); - switch (Z_LVAL_P(zoption_copy)) { - 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); - - if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) { - char *key_str = NULL; - uint key_len = 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))) { - ret = FAILURE; - } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) { - r->content.encoding = estrndup(key_str, key_len - 1); - } - } 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))) { - ret = FAILURE; - } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) { - r->content.encoding = estrndup(key_str, key_len - 1); - } - } 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)) { - 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)) { - 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); - - 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)); - } else { - ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy)); - } - zval_ptr_dtor(&zoption_copy); - } - if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) { - zval *zoption_copy = php_http_ztyp(IS_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); - if (date) { - ret = r->ops->set_header(r, "Last-Modified: %s", date); - efree(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; - 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 ((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 (r->range.status == PHP_HTTP_RANGE_OK) { - if (zend_hash_num_elements(&r->range.values) == 1) { - /* single range */ - 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) - ) { - /* 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); - 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; - - FOREACH_HASH_VAL(pos, &r->range.values, chunk) { - zval **begin, **end; - - if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 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), - 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); - } - } - - 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); - - /* check for ranges */ - if ((body = get_body(r->options TSRMLS_CC))) { - 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); - - 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)) { - 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) - ) { - 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) - ) { - 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"); - return FAILURE; - } - - if (SUCCESS != php_http_env_response_send_body(r)) { - php_error_docref(NULL TSRMLS_CC, 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"); - 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); -} -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); -} -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); -} -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); - 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); - 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); -} -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_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) { - sapi_flush(TSRMLS_C); - } -#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; - } - 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); - - /* 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); - } - zend_hash_destroy(&ctx->header); - zend_list_delete(ctx->stream->rsrc_id); - 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) -{ - HashPosition pos; - 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); - } else { - zval *tmp = php_http_ztyp(IS_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:"))) { - ctx->chunked = 0; - } - } - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - php_http_buffer_appends(buf, PHP_HTTP_CRLF); - zval_ptr_dtor(&tmp); - } - } -} -static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC) -{ - 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); - - /* 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); - 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; - - 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'; - - 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); - } else { - MAKE_STD_ZVAL(zheader); - ZVAL_STRINGL(zheader, header_str, header_len, 0); - - 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; - } -} -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); - 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)) { - 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)) { - 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)) { - 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->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); \ - } \ - } 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); - - php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), 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; - - 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); - - 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); - } - 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); - - set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC); - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return); - - set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC); - 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); - - zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC); - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return); - - set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC); - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return); - - set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC); - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return); - - set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC); - 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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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)); - } -} - -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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return); - - set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC); - 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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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)); - } -} - -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; - double delay = 1; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); - 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; - 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); - - zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); - switch (Z_TYPE_P(zcookie_new)) { - case IS_OBJECT: - if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) { - 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); - 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); - } - zend_restore_error_handling(&zeh TSRMLS_CC); - - set_cookie(getThis(), zcookie_new TSRMLS_CC); - 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)) { - /* 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 - - 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); - 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)) { - 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); - - 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 --git a/php_http_env_response.h b/php_http_env_response.h deleted file mode 100644 index e6a112f..0000000 --- a/php_http_env_response.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - - 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 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 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 --git a/php_http_etag.c b/php_http_etag.c deleted file mode 100644 index 3604ad8..0000000 --- a/php_http_etag.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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 --git a/php_http_etag.h b/php_http_etag.h deleted file mode 100644 index bf6cf49..0000000 --- a/php_http_etag.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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 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 --git a/php_http_exception.c b/php_http_exception.c deleted file mode 100644 index 25a33e6..0000000 --- a/php_http_exception.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - if (ex) { - zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC); - 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); - - /* - * 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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 --git a/php_http_exception.h b/php_http_exception.h deleted file mode 100644 index 969a351..0000000 --- a/php_http_exception.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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__) - -/* 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); \ - if (!(test)) { \ - zend_restore_error_handling(&__zeh TSRMLS_CC); \ - fail; \ - } \ - zend_restore_error_handling(&__zeh TSRMLS_CC); \ - } 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 --git a/php_http_filter.c b/php_http_filter.c deleted file mode 100644 index b6d967b..0000000 --- a/php_http_filter.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - 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 -#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) -#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); \ - if (!__buck) { \ - pefree(__data, this->is_persistent); \ - return PSFS_ERR_FATAL; \ - } \ - \ - php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \ - } - -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); - - 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); - } - - 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); - 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_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_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); - } - - /* 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; - - 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_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); - } - - /* 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_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) -{ - zval **tmp = ¶ms; - 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); - - } - 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 (!(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 (!(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 --git a/php_http_filter.h b/php_http_filter.h deleted file mode 100644 index 21fe4db..0000000 --- a/php_http_filter.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_FILTER_H -#define PHP_HTTP_FILTER_H - -PHP_HTTP_API php_stream_filter_factory php_http_filter_factory; -PHP_MINIT_FUNCTION(http_filter); - -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ - diff --git a/php_http_header.c b/php_http_header.c deleted file mode 100644 index 41601df..0000000 --- a/php_http_header.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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"); - return FAILURE; - } - - if (!php_http_header_parser_init(&ctx TSRMLS_CC)) { - php_http_buffer_dtor(&buf); - php_error_docref(NULL TSRMLS_CC, 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) -{ - 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); - - 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); - - cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval)); - zval_ptr_dtor(&strval); - } - } - } else { - zval *strval = php_http_header_value_to_string(*header TSRMLS_CC); - - cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval)); - zval_ptr_dtor(&strval); - } - } - } -} - -void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC) -{ - php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC); -} - -zval *php_http_header_value_to_string(zval *header TSRMLS_DC) -{ - 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); - } - - return ret; -} - -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; - - 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); - - 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); - 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_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; - - 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)) { - php_http_buffer_appends(&buf, ": "); - php_http_buffer_append(&buf, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - } else { - php_http_buffer_appends(&buf, ":"); - } - zval_ptr_dtor(&zvalue); - - RETURN_PHP_HTTP_BUFFER_VAL(&buf); - } - 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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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 (zend_hash_num_elements(&ht)) { - zval **val, *cpy; - char *str; - uint len; - ulong idx; - - zend_hash_internal_pointer_reset(&ht); - switch (zend_hash_get_current_key_ex(&ht, &str, &len, &idx, 0, NULL)) { - case HASH_KEY_IS_STRING: - zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), str, len - 1 TSRMLS_CC); - break; - case HASH_KEY_IS_LONG: - zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx TSRMLS_CC); - 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); - } - } - 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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); -} - -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; - char *sep_str = NULL; - size_t sep_len = 0; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { - return; - } - if (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")) { - sep_str = "/"; - sep_len = 1; - } else if (!strcasecmp(Z_STRVAL_P(zname), "Accept-Language")) { - sep_str = "-"; - sep_len = 1; - } - 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 ((rs = php_http_negotiate(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), supported, sep_str, sep_len TSRMLS_CC))) { - 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_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; - - INIT_PZVAL(&zctor); - ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"), 0); - - MAKE_STD_ZVAL(zparams_obj); - 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); - 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 (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; - zend_class_entry *ce = NULL; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)) { - 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; - - Z_ADDREF_PP(val); - zvalue = *val; - - MAKE_STD_ZVAL(zkey); - if (key.type == HASH_KEY_IS_LONG) { - ZVAL_LONG(zkey, key.num); - } else { - ZVAL_STRINGL(zkey, key.str, key.len - 1, 1); - } - - MAKE_STD_ZVAL(zho); - object_init_ex(zho, ce); - zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, zkey, zvalue); - - if (key.type == HASH_KEY_IS_LONG) { - zend_hash_index_update(Z_ARRVAL_P(return_value), key.num, (void *) &zho, sizeof(zval *), NULL); - } else { - zend_hash_update(Z_ARRVAL_P(return_value), key.str, key.len, (void *) &zho, sizeof(zval *), NULL); - } - - zval_ptr_dtor(&zvalue); - zval_ptr_dtor(&zkey); - } - } - } - } -} - -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); - - 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 --git a/php_http_header.h b/php_http_header.h deleted file mode 100644 index a2baecb..0000000 --- a/php_http_header.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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 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 zval *php_http_header_value_to_string(zval *header TSRMLS_DC); - -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 --git a/php_http_header_parser.c b/php_http_header_parser.c deleted file mode 100644 index 46551e2..0000000 --- a/php_http_header_parser.c +++ /dev/null @@ -1,489 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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) -{ - int escaped_len; - char *escaped_str; - - escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC); - - 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); - } 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); - } 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); - } - - 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"); - 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)) { - /* new message starting with request/response line */ - if (callback_func) { - callback_func(callback_arg, &headers, &parser->info TSRMLS_CC); - } - 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); - 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); - 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; - 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); - - 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); - } - - 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); - } else { - add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0); - } - 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) -{ - return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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; - } - - if (parser) { - o->parser = parser; - } else { - o->parser = php_http_header_parser_init(NULL TSRMLS_CC); - } - 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; - - return o->zv; -} - -void php_http_header_parser_object_free(void *object TSRMLS_DC) -{ - php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object; - - 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_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); - - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); - - if (Z_TYPE_P(zmsg) != IS_ARRAY) { - zval_dtor(zmsg); - array_init(zmsg); - } - 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_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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); - - if (Z_TYPE_P(zmsg) != IS_ARRAY) { - zval_dtor(zmsg); - array_init(zmsg); - } - parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - 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); - 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.clone_obj = NULL; - - 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("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); - - 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 --git a/php_http_header_parser.h b/php_http_header_parser.h deleted file mode 100644 index ed9ecaf..0000000 --- a/php_http_header_parser.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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; -} 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); - -#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 --git a/php_http_info.c b/php_http_info.c deleted file mode 100644 index 4fb067f..0000000 --- a/php_http_info.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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) -{ - 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); - - /* and nothing than SPACE or NUL after HTTP/X.x */ - if (!php_http_version_parse(&info->http.version, http TSRMLS_CC) - || (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; - 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); - } else { - PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC); - } - 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 --git a/php_http_info.h b/php_http_info.h deleted file mode 100644 index 4f02908..0000000 --- a/php_http_info.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - -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); - -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 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 --git a/php_http_message.c b/php_http_message.c deleted file mode 100644 index c6b03ff..0000000 --- a/php_http_message.c +++ /dev/null @@ -1,2085 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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)->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) -{ - 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); - - return message; -} - -php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type TSRMLS_DC) -{ - 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); - 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); - } - if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1 TSRMLS_CC))) { - 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); - } - - php_http_env_get_request_headers(&message->hdrs TSRMLS_CC); - 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)) { - 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)); - 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"); - 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_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); - - if ((free_msg = !msg)) { - msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC); - } - - 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 *ret = NULL, **header; - 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; - } - } - - 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_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); - - if (php_http_params_parse(¶ms, &popts TSRMLS_CC)) { - zval **cur, **arg; - char *ct_str; - - 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 (php_http_match(ct_str, "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 - ) { - zval **val; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - - 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); - - if (Z_STRLEN_P(bnd)) { - *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd)); - } - zval_ptr_dtor(&bnd); - } - } - } - } - } - } - zend_hash_destroy(¶ms); - zval_ptr_dtor(&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; - size_t size; - - 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))) { - /* 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); - - if (msg->body->boundary) { - char *str; - size_t len; - - if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) { - 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); - } else { - zval_ptr_dtor(&h); - } - } - } 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)) { - /* body->size == 0, so get rid of old Content-Length */ - zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length")); - } - zval_ptr_dtor(&h_cpy); - } -} - -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); -} - -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); - - 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 *dst = php_http_message_copy(one, NULL), *src = php_http_message_copy(two, NULL), *tmp_dst, *tmp_src, *ret = dst; - - 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); - php_http_message_set_info(temp, &info); - zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - - 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); - 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 *)); - - 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 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); - -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); -} -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 void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - 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_get_request_method(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) { - RETVAL_STRING(obj->message->http.info.request.method, 1); - } else { - RETVAL_NULL(); - } -} -static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - 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); - } -} -static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - 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); - } else { - RETVAL_NULL(); - } -} -static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - 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)); - } -} -static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) { - RETVAL_STRING(obj->message->http.info.response.status, 1); - } else { - RETVAL_NULL(); - } -} -static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) { - 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); - } -} -static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - 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) { - 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); - 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) { - 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); -} -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_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - 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 *)); -} -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); - - 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); -} -static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - if (obj->body) { - RETVAL_OBJVAL(obj->body->zv, 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_get_parent_message(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { - if (obj->message->parent) { - RETVAL_OBJVAL(obj->parent->zv, 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); - - if (obj->message->parent) { - zend_objects_store_del_ref_by_handle(obj->parent->zv.handle TSRMLS_CC); - } - Z_OBJ_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); \ - } \ - } while(0) - - -void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC) -{ - int i = 0; - php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - /* count */ - php_http_message_count(i, 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); - - efree(objects); - } else { - RETURN_ZVAL(getThis(), 1, 0); - } -} - -void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC) -{ - 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; - - 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); - - 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) -{ - zval *tmp = NULL; - php_stream *s; - zend_object_value ov; - 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); - 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)) { - 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); - break; - } - /* no break */ - - default: - tmp = php_http_ztyp(IS_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; - 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); - } - if (msg_obj->body) { - zend_objects_store_del_ref_by_handle(msg_obj->body->zv.handle TSRMLS_CC); - } - 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); - } 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->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); -} - -zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC) -{ - return php_http_message_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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; - } - - if (msg) { - o->message = msg; - if (msg->parent) { - php_http_message_object_new_ex(ce, msg->parent, &o->parent TSRMLS_CC); - } - 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->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; - - return o->zv; -} - -zend_object_value php_http_message_object_clone(zval *this_ptr TSRMLS_DC) -{ - 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); - - 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); - - return new_ov; -} - -void php_http_message_object_free(void *object TSRMLS_DC) -{ - php_http_message_object_t *o = (php_http_message_object_t *) object; - - if (o->iterator) { - zval_ptr_dtor(&o->iterator); - o->iterator = NULL; - } - 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); - o->parent = NULL; - } - if (o->body) { - zend_objects_store_del_ref_by_handle(o->body->zv.handle TSRMLS_CC); - o->body = NULL; - } - zend_object_std_dtor((zend_object *) o TSRMLS_CC); - efree(o); -} - -static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) -{ - 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); - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - 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 (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); - } - } else { - return_value = zend_get_std_object_handlers()->read_property(object, member, type PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); - } - - zval_ptr_dtor(©); - - return return_value; -} - -static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) -{ - php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - php_http_message_object_prophandler_t *handler; - zval *copy = php_http_ztyp(IS_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); - } else { - zend_get_std_object_handlers()->write_property(object, member, value PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); - } - - zval_ptr_dtor(©); -} - -static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) -{ - 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; - char *ver_str, *url_str = NULL; - size_t ver_len, url_len = 0; - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - INIT_PZVAL_ARRAY(&array, props); - -#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) \ - 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); \ - } \ - } while(0) - - ASSOC_PROP(long, "type", 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); - - 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)); - 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); - } else { - ASSOC_STRINGL("requestUrl", "", 0); - } - - 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); - break; - - case PHP_HTTP_NONE: - default: - ASSOC_PROP(long, "responseCode", 0); - ASSOC_STRINGL("responseStatus", "", 0); - ASSOC_STRINGL("requestMethod", "", 0); - ASSOC_STRINGL("requestUrl", "", 0); - 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); - - MAKE_STD_ZVAL(body); - if (obj->body) { - ZVAL_OBJVAL(body, obj->body->zv, 1); - } else { - ZVAL_NULL(body); - } - ASSOC_PROP(zval, "body", body); - - MAKE_STD_ZVAL(parent); - if (obj->message->parent) { - ZVAL_OBJVAL(parent, obj->parent->zv, 1); - } else { - ZVAL_NULL(parent); - } - ASSOC_PROP(zval, "parentMessage", parent); - - 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); - zend_error_handling zeh; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy), invalid_arg, return); - - zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh TSRMLS_CC); - 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); - - if (s && php_http_message_parser_init(&p TSRMLS_CC)) { - 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); - - if (!msg && !EG(exception)) { - php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage)); - } - zval_ptr_dtor(&zmessage); - } - - 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); - } - } - zend_restore_error_handling(&zeh TSRMLS_CC); - 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); - - 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); - } -} - -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); - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - php_http_message_object_prophandler_set_body(obj, zbody TSRMLS_CC); - } - 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); - - 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; - 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); - zval *header; - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) { - if (!header_ce) { - RETURN_ZVAL(header, 1, 1); - } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) { - 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); - - argv[0] = &header_name; - 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_dtor(&cb); - - zval_ptr_dtor(&header_name); - zval_ptr_dtor(&header); - - return; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name); - } - } - } - 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_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; - - 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); - 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); - } else { - Z_ADDREF_P(zvalue); - zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); - } - 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); - - 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; - - 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); - 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))) { - convert_to_array(header); - zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL); - zval_ptr_dtor(&header); - } else { - zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); - } - 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); - - 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_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; - - 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); - - 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); - - 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, "")); - 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, "")); - PTR_FREE(tmp); - break; - default: - RETURN_NULL(); - break; - } - Z_TYPE_P(return_value) = IS_STRING; - return; - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - if (!php_http_info_parse(&inf, str TSRMLS_CC)) { - 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_INIT(obj); - - php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL TSRMLS_CC); - RETURN_STRINGL(str, len, 0); - } -} - -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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - php_http_expect(php_http_version_parse(&version, v_str TSRMLS_CC), 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_INIT(obj); - - if (obj->message->type != PHP_HTTP_RESPONSE) { - php_error_docref(NULL TSRMLS_CC, 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_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_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_INIT(obj); - - if (obj->message->type != PHP_HTTP_RESPONSE) { - php_error_docref(NULL TSRMLS_CC, 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); - } 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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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_INIT(obj); - - if (obj->message->type != PHP_HTTP_REQUEST) { - php_error_docref(NULL TSRMLS_CC, 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); - } 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; - 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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_INIT(obj); - - if (obj->message->type != PHP_HTTP_REQUEST) { - php_error_docref(NULL TSRMLS_CC, 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); - } 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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); - - 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); - - 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); -} - -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); - 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_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); - 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); - } -} - -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); - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - fcd.fcz = getThis(); - Z_ADDREF_P(fcd.fcz); - TSRMLS_SET_CTX(fcd.ts); - - 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); - 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_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; - 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); - php_http_message_t *msg; - - if (obj->message) { - php_http_message_dtor(obj->message); - efree(obj->message); - } - if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) { - 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"); - } - } -} - -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_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - 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); -} - -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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC); - 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); - 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); -} - -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); - char *boundary = NULL; - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL)); - - if (zboundary && boundary) { - zval_dtor(zboundary); - ZVAL_STRING(zboundary, boundary, 0); - } - } -} - -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); - - 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); -} - -ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_count, 0, 0, 0) -ZEND_END_ARG_INFO(); -static PHP_METHOD(HttpMessage, count) -{ - 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); - - PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - - php_http_message_count(i, obj->message); - RETURN_LONG(i); - } -} - -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); - - if (obj->iterator) { - zval_ptr_dtor(&obj->iterator); - } - Z_ADDREF_P(zobj); - 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); - - RETURN_BOOL(obj->iterator != NULL); - } -} - -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); - - if (obj->iterator) { - php_http_message_object_t *itr = zend_object_store_get_object(obj->iterator TSRMLS_CC); - - 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); - } else { - zval_ptr_dtor(&obj->iterator); - obj->iterator = NULL; - } - } - } -} - -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); - - RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0); - } -} - -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); - - if (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->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.clone_obj = php_http_message_object_clone; - 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_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_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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - 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); - - 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 --git a/php_http_message.h b/php_http_message.h deleted file mode 100644 index 780ea68..0000000 --- a/php_http_message.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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" - -/* 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 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_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents); -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 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)); \ -} - -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); - -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; -} 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); -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); - -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ - diff --git a/php_http_message_body.c b/php_http_message_body.c deleted file mode 100644 index c80c238..0000000 --- a/php_http_message_body.c +++ /dev/null @@ -1,934 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - -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 *body; - - if (body_ptr && *body_ptr) { - body = *body_ptr; - ++body->refcount; - 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); - } else { - stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff); - php_stream_auto_cleanup(stream); - body->stream_id = php_stream_get_resource_id(stream); - } - TSRMLS_SET_CTX(body->ts); - - 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); - } - 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); - 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); - 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))) { - 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) -{ - 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); -} - -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); - } - - 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)) { - return FAILURE; - } - } - if (files) { - INIT_PZVAL_ARRAY(&tmp, files); - if (SUCCESS != add_recursive_files(body, NULL, &tmp)) { - 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); - - safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC); - - BOUNDARY_OPEN(body); - php_http_message_body_appendf( - body, - "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF - "" PHP_HTTP_CRLF, - safe_name - ); - php_http_message_body_append(body, value_str, value_len); - BOUNDARY_CLOSE(body); - - efree(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); - - 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); - - 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 - ); - php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL); - BOUNDARY_CLOSE(body); - - efree(safe_name); - efree(path_dup); - efree(bname); - - return SUCCESS; -} - -static inline char *format_key(uint type, char *str, ulong num, const char *prefix) { - char *new_key = NULL; - - if (prefix && *prefix) { - if (type == HASH_KEY_IS_STRING) { - spprintf(&new_key, 0, "%s[%s]", prefix, str); - } else { - spprintf(&new_key, 0, "%s[%lu]", prefix, num); - } - } else if (type == HASH_KEY_IS_STRING) { - new_key = estrdup(str); - } 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) -{ - 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); - - 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)) { - efree(str); - ht->nApplyCount--; - return FAILURE; - } - efree(str); - } - --ht->nApplyCount; - } - } 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); - } - - return SUCCESS; -} - -static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value) -{ - zval **zdata = NULL, **zfile, **zname, **ztype; - HashTable *ht; - TSRMLS_FETCH_FROM_CTX(body->ts); - - 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)) - ) { - zval **val; - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - - 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 (SUCCESS != add_recursive_files(body, str, *val)) { - efree(str); - --ht->nApplyCount; - return FAILURE; - } - efree(str); - } - } - --ht->nApplyCount; - } - return SUCCESS; - } else { - php_stream *stream; - zval *zfc = php_http_ztyp(IS_STRING, *zfile); - - if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) { - if (Z_TYPE_PP(zdata) == IS_RESOURCE) { - php_stream_from_zval_no_verify(stream, zdata); - } else { - zval *tmp = php_http_ztyp(IS_STRING, *zdata); - - stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - zval_ptr_dtor(&tmp); - } - } else { - stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL); - } - - if (!stream) { - zval_ptr_dtor(&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); - - efree(key); - zval_ptr_dtor(&znc); - zval_ptr_dtor(&ztc); - zval_ptr_dtor(&zfc); - if (!zdata || Z_TYPE_PP(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) -{ - 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->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); - 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.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); - } - - 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) -{ - return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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; - } - - 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; - - return o->zv; -} - -zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC) -{ - 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_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); - - return new_ov; -} - -void php_http_message_body_object_free(void *object TSRMLS_DC) -{ - php_http_message_body_object_t *obj = object; - - php_http_message_body_free(&obj->body); - zend_object_std_dtor((zend_object *) obj TSRMLS_CC); - efree(obj); -} - -#define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \ - do { \ - if (!obj->body) { \ - obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \ - } \ - } 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); - zval *zstream = NULL; - php_stream *stream; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return); - - if (zstream) { - 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); - } -} - -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_INIT(obj); - - php_http_message_body_to_string(obj->body, &str, &len, 0, 0); - if (str) { - RETURN_STRINGL(str, len, 0); - } - } - 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; - - 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); - php_stream *s = php_stream_memory_open(0, us_str, us_len); - - obj->body = php_http_message_body_init(NULL, s TSRMLS_CC); - } -} - -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; - - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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_INIT(obj); - - 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; - - 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); - - PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); - - fcd.fcz = getThis(); - Z_ADDREF_P(fcd.fcz); - TSRMLS_SET_CTX(fcd.ts); - - 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_INIT(obj); - - zend_list_addref(obj->body->stream_id); - RETVAL_RESOURCE(obj->body->stream_id); - } -} - -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_INIT(obj); - - if (obj->body->boundary) { - RETURN_STRING(obj->body->boundary, 1); - } - } -} - -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; - 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_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_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); - - obj = zend_object_store_get_object(getThis() TSRMLS_CC); - mobj = zend_object_store_get_object(zobj TSRMLS_CC); - - PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); - - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); - php_http_message_body_add_part(obj->body, mobj->message); - zend_restore_error_handling(&zeh TSRMLS_CC); - - 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); - char *etag; - - PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); - - if ((etag = php_http_message_body_etag(obj->body))) { - RETURN_STRING(etag, 0); - } 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; - - 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); - 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); - 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); - } - } - } -} - -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->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.clone_obj = php_http_message_body_object_clone; - zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 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 --git a/php_http_message_body.h b/php_http_message_body.h deleted file mode 100644 index dc2c7a2..0000000 --- a/php_http_message_body.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - 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 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_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 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()); -} - -typedef struct php_http_message_body_object { - zend_object zo; - zend_object_value zv; - php_http_message_body_t *body; -} 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); - -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ - diff --git a/php_http_message_parser.c b/php_http_message_parser.c deleted file mode 100644 index fae16f1..0000000 --- a/php_http_message_parser.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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); - - 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; - - /* 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")); - - /* 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); - } - - 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); - } - } - - /* 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)) - ) { - if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) { - 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; - } - } - 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 (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); - } - 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); - } - } - - 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 (h_cr) { - 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")] == '=' - ) - ) { - char *total_at = NULL, *end_at = NULL; - char *start_at = Z_STRVAL_PP(h_cr) + 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); - 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); - - 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 ((*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); - 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) -{ - return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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; - } - - if (parser) { - o->parser = parser; - } else { - o->parser = php_http_message_parser_init(NULL TSRMLS_CC); - } - o->buffer = php_http_buffer_new(); - - 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; -} - -void php_http_message_parser_object_free(void *object TSRMLS_DC) -{ - php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object; - - 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); -} - -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); - - 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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)); - - zval_dtor(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); - } -} - -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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); - - 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)); - - zval_dtor(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); - } -} - -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); - 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); - - 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 --git a/php_http_message_parser.h b/php_http_message_parser.h deleted file mode 100644 index 0bac9da..0000000 --- a/php_http_message_parser.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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_message_parser_t *parser; -} 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); - -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ - diff --git a/php_http_misc.c b/php_http_misc.c deleted file mode 100644 index 8e2227d..0000000 --- a/php_http_misc.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C)); -} - -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, ...) -{ - HashPosition pos; - unsigned argl = 0; - va_list argv; - - 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; - } - } - va_end(argv); - - return argl; -} - -void php_http_array_copy_strings(void *zpp) -{ - zval **zvpp = ((zval **) zpp); - - *zvpp = php_http_zsep(1, IS_STRING, *zvpp); -} - -int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) -{ - int flags; - char *key = NULL; - HashTable *dst; - zval **data = NULL, *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_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); - } else { - zend_hash_index_find(dst, hash_key->h, (void *) &data); - } - - if (flags & ARRAY_JOIN_STRINGIFY) { - value = php_http_zsep(1, IS_STRING, value); - } else { - Z_ADDREF_P(value); - } - - if (data) { - if (Z_TYPE_PP(data) != IS_ARRAY) { - convert_to_array(*data); - } - 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); - } else { - zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); - } - - 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 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_STRINGIFY) { - value = php_http_zsep(1, IS_STRING, value); - } else { - Z_ADDREF_P(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); - 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 { - zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); - } - } - - 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); - - 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); - 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 --git a/php_http_misc.h b/php_http_misc.h deleted file mode 100644 index a4f579d..0000000 --- a/php_http_misc.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); -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 -#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) -{ - 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; - } - return z; -} - -static inline zval *php_http_ztyp(int type, zval *z) -{ - SEPARATE_ARG_IF_REF(z); - return (Z_TYPE_P(z) == type) ? z : php_http_zconv(type, z); -} - -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; - } -} - -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) -{ - zend_ini_entry *ini_entry; - - if (SUCCESS == zend_hash_find(EG(ini_directives), name_str, name_len + 1, (void *) &ini_entry)) { - if (orig && ini_entry->modified) { - *value_str = ini_entry->orig_value; - *value_len = (size_t) ini_entry->orig_value_length; - } else { - *value_str = ini_entry->value; - *value_len = (size_t) ini_entry->value_length; - } - return SUCCESS; - } - return FAILURE; -} - -/* return object(values) */ -#define RETVAL_OBJECT(o, addref) \ - RETVAL_OBJVAL((o)->value.obj, 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, ...); - -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} - -static inline void php_http_array_hashkey_stringify(php_http_array_hashkey_t *key) -{ - if (key->type != HASH_KEY_IS_STRING) { - key->len = spprintf(&key->str, 0, "%lu", key->num) + 1; - } -} - -static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key) -{ - if (key->type != HASH_KEY_IS_STRING || key->dup) { - PTR_FREE(key->str); - } -} - -#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_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) - -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); - -/* 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; - 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 --git a/php_http_negotiate.c b/php_http_negotiate.c deleted file mode 100644 index a74875b..0000000 --- a/php_http_negotiate.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - zval result, *first, *second; - - first = *((zval **) (*((Bucket **) a))->pData); - second= *((zval **) (*((Bucket **) b))->pData); - - if (numeric_compare_function(&result, first, second TSRMLS_CC) != 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 -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; - - 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) -{ - 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); - 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); - - if (match > best_match) { - best_match = match; - q = val; - } - } - } - - 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); - } - - zval_ptr_dtor(&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 *result = NULL; - - if (value_str && value_len) { - unsigned i = 0; - zval arr, **val, **arg, **zq; - HashPosition pos; - HashTable params; - php_http_array_hashkey_t key = php_http_array_hashkey_init(1); - 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); - efree(opts.input.str); - - INIT_PZVAL(&arr); - array_init(&arr); - - FOREACH_HASH_KEYVAL(pos, ¶ms, 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); - } else { - q = 1.0 - ++i / 100.0; - } - - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_double_ex(&arr, key.str, key.len, q); - } else { - add_index_double(&arr, key.num, q); - } - - PTR_FREE(key.str); - } - -#if 0 - zend_print_zval_r(&arr, 1 TSRMLS_CC); -#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_destroy(¶ms); - zval_dtor(&arr); - zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC); - } - - 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 --git a/php_http_negotiate.h b/php_http_negotiate.h deleted file mode 100644 index f7405b5..0000000 --- a/php_http_negotiate.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); - -static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC) -{ - HashTable *result = NULL; - size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC); - - if (value) { - result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC); - } - PTR_FREE(value); - - return result; -} - -static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC) -{ - HashTable *result = NULL; - size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC); - - if (value) { - result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); - } - PTR_FREE(value); - - return result; -} - -static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC) -{ - HashTable *result = NULL; - size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC); - - if (value) { - result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); - } - PTR_FREE(value); - - return result; -} - -static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC) -{ - HashTable *result = NULL; - size_t length; - char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC); - - if (value) { - result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC); - } - PTR_FREE(value); - - return result; -} - -#define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \ - { \ - zval **value; \ - \ - zend_hash_internal_pointer_reset((supported)); \ - if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \ - 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; \ - \ - 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); \ - } \ - } - -#define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \ - { \ - char *key; \ - uint key_len; \ - 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); \ - } 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_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))) { \ - 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 --git a/php_http_object.c b/php_http_object.c deleted file mode 100644 index d57388d..0000000 --- a/php_http_object.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - return php_http_object_new_ex(ce, NULL, NULL TSRMLS_CC); -} - -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 *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); - - if (ptr) { - *ptr = o; - } - - 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->zv; -} - -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_object_value ov; - - 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); - return FAILURE; - } - - ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC); - if (ovp) { - *ovp = ov; - } - return SUCCESS; -} - -static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC) -{ -#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; - 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); - - 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; - } -} - -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 rv; - zval *retval = NULL; - - Z_ADDREF_P(zobject); - cb->fci.object_ptr = zobject; - cb->fcc.object_ptr = zobject; - - cb->fci.retval_ptr_ptr = 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); - } - - rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC); - - zval_ptr_dtor(&zobject); - if (!retval_ptr && retval) { - zval_ptr_dtor(&retval); - } - - return rv; -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ - diff --git a/php_http_object.h b/php_http_object.h deleted file mode 100644 index 3f75932..0000000 --- a/php_http_object.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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 { - 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); - -typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC); - -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); - -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); -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 --git a/php_http_options.c b/php_http_options.c deleted file mode 100644 index 59b9c5f..0000000 --- a/php_http_options.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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); - - return registry; -} - -ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata) -{ - HashPosition pos; - zval *val; - php_http_option_t *opt; - - FOREACH_HASH_VAL(pos, ®istry->options, opt) { - 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; - } - } - 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; - - 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.type = type; - opt.option = option; - - INIT_ZVAL(opt.defval); - switch ((opt.type = type)) { - case IS_BOOL: - ZVAL_BOOL(&opt.defval, 0); - break; - - case IS_LONG: - ZVAL_LONG(&opt.defval, 0); - break; - - case IS_STRING: - ZVAL_STRINGL(&opt.defval, NULL, 0, 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; -} - -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 NULL; -} - - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/php_http_options.h b/php_http_options.h deleted file mode 100644 index 2475383..0000000 --- a/php_http_options.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - - ulong option; - zend_uchar type; - unsigned flags; - zval defval; - - php_http_option_set_callback_t setter; -}; - -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 --git a/php_http_params.c b/php_http_params.c deleted file mode 100644 index 5adeb91..0000000 --- a/php_http_params.c +++ /dev/null @@ -1,1283 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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, - 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; - } current; - unsigned quotes:1; - unsigned escape:1; - unsigned rfc5987:1; -} php_http_params_state_t; - -static inline void sanitize_escaped(zval *zv TSRMLS_DC) -{ - 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); - } - - php_stripcslashes(Z_STRVAL_P(zv), &Z_STRLEN_P(zv)); -} - -static inline void quote_string(zval *zv, zend_bool force TSRMLS_DC) -{ - int len = Z_STRLEN_P(zv); - - 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); - - 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); - - str[0] = '"'; - memcpy(&str[1], Z_STRVAL_P(zv), Z_STRLEN_P(zv)); - str[len-1] = '"'; - str[len] = '\0'; - - zval_dtor(&tmp); - ZVAL_STRINGL(zv, str, len, 0); - } -} - -static inline void prepare_escaped(zval *zv TSRMLS_DC) -{ - if (Z_TYPE_P(zv) == IS_STRING) { - quote_string(zv, 0 TSRMLS_CC); - } else { - zval_dtor(zv); - ZVAL_EMPTY_STRING(zv); - } -} - -static inline void sanitize_urlencoded(zval *zv TSRMLS_DC) -{ - 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) -{ - int len; - char *str = php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &len); - - zval_dtor(zv); - ZVAL_STRINGL(zv, str, len, 0); -} - -static void sanitize_dimension(zval *zv TSRMLS_DC) -{ - zval *arr = NULL, *tmp = NULL, **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); - 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)); - return; - } - if (ptr - var == 0) { - ++var; - break; - } - /* no break */ - - case ']': - - MAKE_STD_ZVAL(tmp); - 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); - *ptr = chr; - } else { - zend_hash_next_index_insert(Z_ARRVAL_PP(cur), (void *) &tmp, sizeof(zval *), (void *) &cur); - } - - var = NULL; - break; - } - - ++ptr; - } - - if (zend_hash_num_elements(Z_ARRVAL_P(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); - } 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 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) -{ - HashTable *ht = HASH_OF(zvalue); - HashPosition pos; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0); - zval **val; - php_http_buffer_t prefix; - - if (!ht->nApplyCount++) { - 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) { - /* 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); - } else { - php_http_buffer_appendf(&prefix, "%lu", key.num); - } - 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); - } else { - zval *cpy = php_http_ztyp(IS_STRING, *val); - - 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); - } - - php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used); - } - 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) -{ - char *eos; - - zval_dtor(zv); - php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); - - if (flags & PHP_HTTP_PARAMS_ESCAPED) { - sanitize_escaped(zv TSRMLS_CC); - } - - 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); - } - - if (flags & PHP_HTTP_PARAMS_DIMENSION) { - sanitize_dimension(zv TSRMLS_CC); - } -} - -static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 TSRMLS_DC) -{ - 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); - } -} - -static inline void sanitize_rfc5988(char *str, size_t len, zval *zv TSRMLS_DC) -{ - zval_dtor(zv); - php_trim(str, len, " ><", 3, zv, 3 TSRMLS_CC); -} - -static inline void prepare_rfc5988(zval *zv TSRMLS_DC) -{ - 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); -} - -static inline void sanitize_value(unsigned flags, char *str, size_t len, zval *zv, zend_bool rfc5987 TSRMLS_DC) -{ - char *language = NULL; - zend_bool latin1 = 0; - - zval_dtor(zv); - php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); - - if (rfc5987) { - sanitize_rfc5987(zv, &language, &latin1 TSRMLS_CC); - } - - if (flags & PHP_HTTP_PARAMS_ESCAPED) { - sanitize_escaped(zv TSRMLS_CC); - } - - if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) { - sanitize_urlencoded(zv TSRMLS_CC); - } - - if (rfc5987 && language) { - zval *tmp; - - if (latin1) { - utf8encode(zv); - } - - MAKE_STD_ZVAL(tmp); - ZVAL_COPY_VALUE(tmp, zv); - array_init(zv); - add_assoc_zval(zv, language, tmp); - PTR_FREE(language); - } -} - -static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len TSRMLS_DC) -{ - zval zv; - - INIT_PZVAL(&zv); - ZVAL_STRINGL(&zv, old_key, old_len, 1); - - if (flags & PHP_HTTP_PARAMS_URLENCODED) { - prepare_urlencoded(&zv TSRMLS_CC); - } - - if (flags & PHP_HTTP_PARAMS_ESCAPED) { - if (flags & PHP_HTTP_PARAMS_RFC5988) { - prepare_rfc5988(&zv TSRMLS_CC); - } else { - prepare_escaped(&zv TSRMLS_CC); - } - } - - *new_key = Z_STRVAL(zv); - *new_len = Z_STRLEN(zv); -} - -static inline void prepare_value(unsigned flags, zval *zv TSRMLS_DC) -{ - if (flags & PHP_HTTP_PARAMS_URLENCODED) { - prepare_urlencoded(zv TSRMLS_CC); - } - - if (flags & PHP_HTTP_PARAMS_ESCAPED) { - prepare_escaped(zv TSRMLS_CC); - } -} - -static void merge_param(HashTable *params, zval *zdata, zval ***current_param, zval ***current_args TSRMLS_DC) -{ - zval **ptr, **zdata_ptr; - php_http_array_hashkey_t hkey = php_http_array_hashkey_init(0); - -#if 0 - { - zval tmp; - INIT_PZVAL_ARRAY(&tmp, params); - fprintf(stderr, "params = "); - zend_print_zval_r(&tmp, 1 TSRMLS_CC); - 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); - - 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)) - ) { - 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); - *current_args = args; - - if (hkey.type == HASH_KEY_IS_STRING) { - zend_hash_update(params, hkey.str, hkey.len, (void *) &tmp, sizeof(zval *), (void *) &ptr); - } else { - zend_hash_index_update(params, hkey.num, (void *) &tmp, sizeof(zval *), (void *) &ptr); - } - } else { - /* merge */ - if (hkey.type == HASH_KEY_IS_STRING) { - zend_hash_find(params, hkey.str, hkey.len, (void *) &ptr); - } else { - zend_hash_index_find(params, hkey.num, (void *) &ptr); - } - - 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) - ) { - /* - * params = [arr => [value => [0 => 1]]] - * ^- ptr - * zdata = [arr => [0 => NULL]] - * ^- zdata_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) { - - /* 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)) { - 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); - break; - } - } else { - if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(ptr), hkey.num, (void *) &ptr)) { - 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); - break; - } else { - Z_ADDREF_PP(test_ptr); - zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &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); - } - 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); - } else { - zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr); - } - break; - } - } - - } - } - - /* bubble up */ - while (Z_TYPE_PP(ptr) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(ptr), (void *) &ptr)); - *current_param = ptr; -} - -static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts TSRMLS_DC) -{ - 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); - } - state->rfc5987 = 0; - } else if (state->arg.str) { - if (0 < (state->arg.len = state->input.str - state->arg.str)) { - 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); - state->rfc5987 = rfc5987; - if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) { - MAKE_STD_ZVAL(val); - ZVAL_TRUE(val); - - if (rfc5987) { - 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); - } else { - 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); - } - } 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); - } - } - zval_dtor(&key); - } - } else if (state->param.str) { - if (0 < (state->param.len = state->input.str - state->param.str)) { - zval *prm, *arg, *val, *key; - zend_bool rfc5987 = 0; - - MAKE_STD_ZVAL(key); - ZVAL_NULL(key); - if (opts->flags & PHP_HTTP_PARAMS_RFC5988) { - sanitize_rfc5988(state->param.str, state->param.len, key TSRMLS_CC); - } else { - sanitize_key(opts->flags, state->param.str, state->param.len, key, &rfc5987 TSRMLS_CC); - 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); - } else { - 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); - } else { - zend_hash_update(Z_ARRVAL_P(prm), "value", sizeof("value"), (void *) &val, sizeof(zval *), (void *) &state->current.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); - } - 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) -{ - 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) -{ - 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); - 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); - - skip_sep(sep_len, &state, opts->param, opts->arg, opts->val TSRMLS_CC); - - /* 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); - - skip_sep(sep_len, &state, NULL, opts->arg, opts->val TSRMLS_CC); - - /* 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); - - skip_sep(sep_len, &state, NULL, NULL, opts->val TSRMLS_CC); - - 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); - - 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) -{ - 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); - 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) -{ - HashTable *ht = HASH_OF(zvalue); - zval **zdata, *tmp; - php_http_array_hashkey_t key = php_http_array_hashkey_init(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)) - ) { - php_http_array_hashkey_stringify(&key); - 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); - - 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)); - 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) -{ - 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); - 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) -{ - zval *tmp = php_http_zsep(1, IS_STRING, zv); - - quote_string(tmp, 1 TSRMLS_CC); - php_http_buffer_append(buf, vss, vsl); - php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); - - zval_ptr_dtor(&tmp); -} - -static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) -{ - if (Z_TYPE_P(zvalue) != IS_BOOL) { - zval *tmp = php_http_zsep(1, IS_STRING, zvalue); - - 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)); - - zval_ptr_dtor(&tmp); - } else if (!Z_BVAL_P(zvalue)) { - php_http_buffer_append(buf, vss, vsl); - php_http_buffer_appends(buf, "0"); - } -} - -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) -{ - 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; - zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*"); - - if (!rfc5987) { - shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); - } - FOREACH_KEYVAL(pos, zvalue, 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); - } else { - shift_arg(buf, key.str, key.len-1, *val, ass, asl, vss, vsl, flags TSRMLS_CC); - } - php_http_array_hashkey_stringfree(&key); - } - } else { - shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); - - 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); - return; - } - break; - } - } - - shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); - } -} - -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) -{ - 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); - 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); - } else { - shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags TSRMLS_CC); - } - } else { - if (flags & PHP_HTTP_PARAMS_RFC5988) { - shift_rfc5988(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); - } else { - shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); - } - shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); - } -} - -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) -{ - zval **zparam; - HashPosition pos, pos1; - php_http_array_hashkey_t key = php_http_array_hashkey_init(0), key1 = php_http_array_hashkey_init(0); - zend_bool rfc5987 = 0; - - if (!buf) { - buf = php_http_buffer_init(NULL); - } - - FOREACH_HASH_KEYVAL(pos, params, key, zparam) { - zval **zvalue, **zargs; - - if (Z_TYPE_PP(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)) { - 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); - - if (Z_TYPE_PP(zparam) == IS_ARRAY && SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("arguments"), (void *) &zvalue)) { - if (zvalue == zparam) { - continue; - } - 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")) { - 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_buffer_shrink(buf); - php_http_buffer_fix(buf); - - return buf; -} - -php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC) -{ - zval **sep; - HashPosition pos; - php_http_params_token_t **ret, **tmp; - - if (!zv) { - return NULL; - } - - zv = php_http_ztyp(IS_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); - - if (Z_STRLEN_P(zt)) { - *tmp = emalloc(sizeof(**tmp)); - (*tmp)->str = estrndup(Z_STRVAL_P(zt), (*tmp)->len = Z_STRLEN_P(zt)); - ++tmp; - } - zval_ptr_dtor(&zt); - } - zval_ptr_dtor(&zv); - - *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; - zend_error_handling zeh; - - 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); - - zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); - { - switch (ZEND_NUM_ARGS()) { - case 5: - zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags TSRMLS_CC); - /* no break */ - case 4: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC); - /* no break */ - case 3: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC); - /* no break */ - case 2: - zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC); - /* 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); - break; - default: - zcopy = php_http_ztyp(IS_STRING, zparams); - if (Z_STRLEN_P(zcopy)) { - 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 - }; - - 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); - - 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); - 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); - } - } - zend_restore_error_handling(&zeh TSRMLS_CC); -} - -ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0) -ZEND_END_ARG_INFO(); -PHP_METHOD(HttpParams, toArray) -{ - zval *zparams; - - if (SUCCESS != zend_parse_parameters_none()) { - return; - } - zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC); - 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; - 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)); - - 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); - } else { - zpsep = php_http_ztyp(IS_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); - } else { - zasep = php_http_ztyp(IS_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); - } else { - zvsep = php_http_ztyp(IS_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); - - zval_ptr_dtor(&zparams); - zval_ptr_dtor(&zpsep); - zval_ptr_dtor(&zasep); - zval_ptr_dtor(&zvsep); - zval_ptr_dtor(&zflags); - - RETVAL_PHP_HTTP_BUFFER_VAL(&buf); -} - -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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { - return; - } - - zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); - - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - RETVAL_BOOL(Z_TYPE_PP(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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { - return; - } - - zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); - - if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { - 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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { - return; - } - - zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); - - 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); -} - -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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &nvalue)) { - return; - } - - zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); - - 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); - } else { - new_zparam = nvalue; - Z_ADDREF_P(new_zparam); - } - add_assoc_zval_ex(zparams, name_str, name_len + 1, new_zparam); - } else { - 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); - } else { - MAKE_STD_ZVAL(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); - } - } else { - zval *tmp = php_http_ztyp(IS_STRING, nvalue), *arr; - - 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); - } - - 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->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); - - 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 --git a/php_http_params.h b/php_http_params.h deleted file mode 100644 index e1ebe27..0000000 --- a/php_http_params.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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; - 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 php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC); -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 --git a/php_http_querystring.c b/php_http_querystring.c deleted file mode 100644 index d72337f..0000000 --- a/php_http_querystring.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - zval *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); - } - - php_http_querystring_update(qa, params, NULL TSRMLS_CC); - zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), qa TSRMLS_CC); - zval_ptr_dtor(&qa); -} - -static inline void php_http_querystring_str(zval *instance, zval *return_value TSRMLS_DC) -{ - zval *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC); - - if (Z_TYPE_P(qa) == IS_ARRAY) { - php_http_querystring_update(qa, NULL, return_value TSRMLS_CC); - } 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) -{ - zval **arrval, *qarray = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); - - 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); - } else { - RETVAL_ZVAL(*arrval, 1, 0); - } - - if (del) { - 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); - 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) -{ - 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); - - 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); - 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); - } - 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); - return FAILURE; - } - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_stringl_ex(dst, xkey, xlen+1, xlate_str, xlate_len, 0); - } else { - add_index_stringl(dst, key.num, xlate_str, xlate_len, 0); - } - } else if (Z_TYPE_PP(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); - } else { - add_index_zval(dst, key.num, subarray); - } - if (SUCCESS != php_http_querystring_xlate(subarray, *entry, ie, oe TSRMLS_CC)) { - if (key.type == HASH_KEY_IS_STRING) { - efree(xkey); - } - return FAILURE; - } - } - - if (key.type == HASH_KEY_IS_STRING) { - efree(xkey); - } - } - return SUCCESS; -} -#endif /* HAVE_ICONV */ - -ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC) -{ - php_http_querystring_set(instance, params, 0 TSRMLS_CC); - return SUCCESS; -} - -static int apply_querystring(void *pData TSRMLS_DC) -{ - zval **val = pData; - - if (Z_TYPE_PP(val) == IS_ARRAY) { - zval **zvalue; - - 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); - } - } - - 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 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; - - MAKE_STD_ZVAL(arr); - array_init_size(arr, asi_len); - - do { - add_next_index_stringl(arr, asi_str++, 1, 1); - } while (*asi_str); - - opts.param = php_http_params_separator_init(arr TSRMLS_CC); - - zval_ptr_dtor(&arr); - } - - MAKE_STD_ZVAL(opts.defval); - ZVAL_NULL(opts.defval); - - if (php_http_params_parse(ht, &opts TSRMLS_CC)) { - zend_hash_apply(ht, apply_querystring TSRMLS_CC); - 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) -{ - /* 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; - - 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); - } else if (Z_TYPE_P(params) == IS_OBJECT || Z_TYPE_P(params) == IS_ARRAY) { - ptr = HASH_OF(params); - } else { - zv_ptr = php_http_ztyp(IS_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); - } - - FOREACH_HASH_KEYVAL(pos, ptr, key, params_entry) { - /* only public properties */ - if (key.type != HASH_KEY_IS_STRING || *key.str) { - if (Z_TYPE_PP(params_entry) == IS_NULL) { - /* - * delete - */ - if (key.type == HASH_KEY_IS_STRING) { - zend_hash_del(Z_ARRVAL_P(qarray), key.str, key.len); - } else { - zend_hash_index_del(Z_ARRVAL_P(qarray), key.num); - } - } 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)))) { - /* - * update - */ - zval equal, *entry = NULL; - - /* 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 (entry) { - if (key.type == HASH_KEY_IS_STRING) { - zend_hash_update(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &entry, sizeof(zval *), NULL); - } else { - zend_hash_index_update(Z_ARRVAL_P(qarray), key.num, (void *) &entry, sizeof(zval *), NULL); - } - } - } else { - zval *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); - } else { - Z_ADDREF_PP(params_entry); - entry = *params_entry; - } - if (key.type == HASH_KEY_IS_STRING) { - add_assoc_zval_ex(qarray, key.str, key.len, entry); - } else { - add_index_zval(qarray, key.num, entry); - } - } - } - } - /* clean up */ - if (zv_ptr) { - zval_ptr_dtor(&zv_ptr); - } - 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)) { - zval_dtor(outstring); - ZVAL_STRINGL(outstring, s, l, 0); - } else { - php_error_docref(NULL TSRMLS_CC, 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); - - 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_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getGlobalInstance, 0, 0, 0) -ZEND_END_ARG_INFO(); -PHP_METHOD(HttpQueryString, getGlobalInstance) -{ - zval *instance; - - 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); - - 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); - - 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); - - 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); - } - } - - 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; - - 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); - - 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_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); -} - -ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toArray, 0, 0, 0) -ZEND_END_ARG_INFO(); -PHP_METHOD(HttpQueryString, toArray) -{ - zval *zqa; - - if (SUCCESS != zend_parse_parameters_none()) { - return; - } - - zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); - 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; - 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 (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); - } else { - php_http_querystring_str(getThis(), return_value TSRMLS_CC); - } - } -} - -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)) { - return; - } - - php_http_querystring_set(getThis(), params, QS_MERGE TSRMLS_CC); - 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; - zend_error_handling zeh; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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_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; \ - 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); \ - } \ -} -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; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)); - - php_http_expect(SUCCESS == php_http_querystring_xlate(na, qa, ie, oe TSRMLS_CC), bad_conversion, - zval_ptr_dtor(&na); - zval_ptr_dtor(&qa); - return; - ); - - php_http_querystring_set(getThis(), na, 0 TSRMLS_CC); - 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); -} - -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)) { - return; - } - - if (Z_TYPE_P(serialized) == IS_STRING) { - php_http_querystring_set(getThis(), serialized, 0 TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, 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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { - return; - } - - qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); - - 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); - } - } -} - -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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &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); - 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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { - return; - } - - qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); - - 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); - } - } - 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; - - if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { - 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); - 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->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_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_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); - - 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 --git a/php_http_querystring.h b/php_http_querystring.h deleted file mode 100644 index 3391e91..0000000 --- a/php_http_querystring.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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); -#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); - -typedef php_http_object_t php_http_querystring_object_t; - -#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 --git a/php_http_response_codes.h b/php_http_response_codes.h deleted file mode 100644 index 82157f9..0000000 --- a/php_http_response_codes.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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-2015, Michael Wallner | - +--------------------------------------------------------------------+ -*/ - -#ifndef PHP_HTTP_RESPONSE_CODE -# define PHP_HTTP_RESPONSE_CODE(code, status) -#endif - -PHP_HTTP_RESPONSE_CODE(100, "Continue") -PHP_HTTP_RESPONSE_CODE(101, "Switching Protocols") -PHP_HTTP_RESPONSE_CODE(102, "Processing") -PHP_HTTP_RESPONSE_CODE(200, "OK") -PHP_HTTP_RESPONSE_CODE(201, "Created") -PHP_HTTP_RESPONSE_CODE(202, "Accepted") -PHP_HTTP_RESPONSE_CODE(203, "Non-Authoritative Information") -PHP_HTTP_RESPONSE_CODE(204, "No Content") -PHP_HTTP_RESPONSE_CODE(205, "Reset Content") -PHP_HTTP_RESPONSE_CODE(206, "Partial Content") -PHP_HTTP_RESPONSE_CODE(207, "Multi-Status") -PHP_HTTP_RESPONSE_CODE(208, "Already Reported") -PHP_HTTP_RESPONSE_CODE(226, "IM Used") -PHP_HTTP_RESPONSE_CODE(300, "Multiple Choices") -PHP_HTTP_RESPONSE_CODE(301, "Moved Permanently") -PHP_HTTP_RESPONSE_CODE(302, "Found") -PHP_HTTP_RESPONSE_CODE(303, "See Other") -PHP_HTTP_RESPONSE_CODE(304, "Not Modified") -PHP_HTTP_RESPONSE_CODE(305, "Use Proxy") -PHP_HTTP_RESPONSE_CODE(307, "Temporary Redirect") -PHP_HTTP_RESPONSE_CODE(308, "Permanent Redirect") -PHP_HTTP_RESPONSE_CODE(400, "Bad Request") -PHP_HTTP_RESPONSE_CODE(401, "Unauthorized") -PHP_HTTP_RESPONSE_CODE(402, "Payment Required") -PHP_HTTP_RESPONSE_CODE(403, "Forbidden") -PHP_HTTP_RESPONSE_CODE(404, "Not Found") -PHP_HTTP_RESPONSE_CODE(405, "Method Not Allowed") -PHP_HTTP_RESPONSE_CODE(406, "Not Acceptable") -PHP_HTTP_RESPONSE_CODE(407, "Proxy Authentication Required") -PHP_HTTP_RESPONSE_CODE(408, "Request Timeout") -PHP_HTTP_RESPONSE_CODE(409, "Conflict") -PHP_HTTP_RESPONSE_CODE(410, "Gone") -PHP_HTTP_RESPONSE_CODE(411, "Length Required") -PHP_HTTP_RESPONSE_CODE(412, "Precondition Failed") -PHP_HTTP_RESPONSE_CODE(413, "Request Entity Too Large") -PHP_HTTP_RESPONSE_CODE(414, "Request URI Too Long") -PHP_HTTP_RESPONSE_CODE(415, "Unsupported Media Type") -PHP_HTTP_RESPONSE_CODE(416, "Requested Range Not Satisfiable") -PHP_HTTP_RESPONSE_CODE(417, "Expectation Failed") -PHP_HTTP_RESPONSE_CODE(422, "Unprocessible Entity") -PHP_HTTP_RESPONSE_CODE(423, "Locked") -PHP_HTTP_RESPONSE_CODE(424, "Failed Dependency") -PHP_HTTP_RESPONSE_CODE(426, "Upgrade Required") -PHP_HTTP_RESPONSE_CODE(428, "Precondition Required") -PHP_HTTP_RESPONSE_CODE(429, "Too Many Requests") -PHP_HTTP_RESPONSE_CODE(431, "Request Header Fields Too Large") -PHP_HTTP_RESPONSE_CODE(500, "Internal Server Error") -PHP_HTTP_RESPONSE_CODE(501, "Not Implemented") -PHP_HTTP_RESPONSE_CODE(502, "Bad Gateway") -PHP_HTTP_RESPONSE_CODE(503, "Service Unavailable") -PHP_HTTP_RESPONSE_CODE(504, "Gateway Timeout") -PHP_HTTP_RESPONSE_CODE(505, "HTTP Version Not Supported") -PHP_HTTP_RESPONSE_CODE(506, "Variant Also Negotiates") -PHP_HTTP_RESPONSE_CODE(507, "Insufficient Storage") -PHP_HTTP_RESPONSE_CODE(508, "Loop Detected") -PHP_HTTP_RESPONSE_CODE(510, "Not Extended") -PHP_HTTP_RESPONSE_CODE(511, "Network Authentication Required") - - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/php_http_url.c b/php_http_url.c deleted file mode 100644 index 81b2d95..0000000 --- a/php_http_url.c +++ /dev/null @@ -1,1749 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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); - 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)) { - 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); - 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 *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); - - old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV TSRMLS_CC); - 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_NULL(&qstr); - php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC); - - 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) -{ - zval *zcpy; - 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); - } - - return purl; -} - -php_http_url_t *php_http_url_from_struct(HashTable *ht) -{ - 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); - 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); - } - if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - 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 (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) { - zval *cpy = php_http_ztyp(IS_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); - } - - return url(buf); -} - -HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC) -{ - zval arr; - - 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)); - break; - } - } else { - INIT_PZVAL(&arr); - array_init(&arr); - } - - if (url) { - if (url->scheme) { - add_assoc_string(&arr, "scheme", url->scheme, 1); - } - if (url->user) { - add_assoc_string(&arr, "user", url->user, 1); - } - if (url->pass) { - add_assoc_string(&arr, "pass", url->pass, 1); - } - if (url->host) { - add_assoc_string(&arr, "host", url->host, 1); - } - if (url->port) { - add_assoc_long(&arr, "port", (long) url->port); - } - if (url->path) { - add_assoc_string(&arr, "path", url->path, 1); - } - if (url->query) { - add_assoc_string(&arr, "query", url->query, 1); - } - if (url->fragment) { - add_assoc_string(&arr, "fragment", url->fragment, 1); - } - } - - return Z_ARRVAL(arr); -} - -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) -{ - 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); - - 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)) { - 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) -{ - 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)) { - 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, - "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, - "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, - "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, - "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) -{ - 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); - 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); - 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)); - 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)); - 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)) { - 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)) { - return FAILURE; - } -#endif - } else { - php_error_docref(NULL TSRMLS_CC, 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); - 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"); - 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"); - return FAILURE; - } -#endif - } else { - php_error_docref(NULL TSRMLS_CC, 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"); - 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) -{ - 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); - 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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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) -{ - 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); - 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); - efree(state); - return NULL; - } - - if (!parse_fragment(state)) { - php_error_docref(NULL TSRMLS_CC, 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) -{ - 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, - "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_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); - - zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); - { - 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); - if (!new_purl) { - zend_restore_error_handling(&zeh TSRMLS_CC); - return; - } - } - if (old_url) { - old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC); - if (!old_purl) { - if (new_purl) { - php_http_url_free(&new_purl); - } - zend_restore_error_handling(&zeh TSRMLS_CC); - return; - } - } - - res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); - php_http_url_to_struct(res_purl, getThis() TSRMLS_CC); - - 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_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_error_handling zeh; - - php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return); - - zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); - { - 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); - if (!new_purl) { - zend_restore_error_handling(&zeh TSRMLS_CC); - 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); - - res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); - php_http_url_to_struct(res_purl, return_value TSRMLS_CC); - - 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_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_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_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); - -#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); -#endif - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC); -#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); -#endif - zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC); - - 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 --git a/php_http_url.h b/php_http_url.h deleted file mode 100644 index 636efb5..0000000 --- a/php_http_url.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_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 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); - -static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC) -{ - php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC); -} - -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 --git a/php_http_utf8.h b/php_http_utf8.h deleted file mode 100644 index f900803..0000000 --- a/php_http_utf8.h +++ /dev/null @@ -1,670 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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_UTF8_H -#define PHP_HTTP_UTF8_H - -typedef struct utf8_range { - unsigned int start; - unsigned int end; - unsigned char step; -} utf8_range_t; - -static const unsigned char utf8_mblen[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 -}; - -static const unsigned char utf8_mask[] = { - 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 -}; - -static const utf8_range_t utf8_ranges[] = { -/* BEGIN::UTF8TABLE */ - { 0x0041, 0x005A, 1}, - { 0x0061, 0x007A, 1}, - { 0x00AA, 0, 0}, - { 0x00B5, 0, 0}, - { 0x00BA, 0, 0}, - { 0x00C0, 0x00D6, 1}, - { 0x00D8, 0x00F6, 1}, - { 0x00F8, 0x00FF, 1}, - { 0x0100, 0x017F, 1}, - { 0x0180, 0x024F, 1}, - { 0x0250, 0x02AF, 1}, - { 0x02B0, 0x02C1, 1}, - { 0x02C6, 0x02D1, 1}, - { 0x02E0, 0x02E4, 1}, - { 0x02EE, 0, 0}, - { 0x0345, 0, 0}, - { 0x0370, 0x0373, 1}, - { 0x0376, 0x0377, 1}, - { 0x037A, 0x037D, 1}, - { 0x0386, 0, 0}, - { 0x0388, 0x038A, 1}, - { 0x038C, 0, 0}, - { 0x038E, 0x03A1, 1}, - { 0x03A3, 0x03CE, 1}, - { 0x03D0, 0x03F5, 1}, - { 0x03F7, 0x03FF, 1}, - { 0x0400, 0x0481, 1}, - { 0x048A, 0x04FF, 1}, - { 0x0500, 0x0523, 1}, - { 0x0531, 0x0556, 1}, - { 0x0559, 0, 0}, - { 0x0561, 0x0587, 1}, - { 0x05D0, 0x05EA, 1}, - { 0x05F0, 0x05F2, 1}, - { 0x0621, 0x064A, 1}, - { 0x066E, 0x066F, 1}, - { 0x0671, 0x06D3, 1}, - { 0x06D5, 0, 0}, - { 0x06E5, 0x06E6, 1}, - { 0x06EE, 0x06EF, 1}, - { 0x06FA, 0x06FC, 1}, - { 0x06FF, 0, 0}, - { 0x0710, 0, 0}, - { 0x0712, 0x072F, 1}, - { 0x074D, 0x074F, 1}, - { 0x0750, 0x077F, 1}, - { 0x0780, 0x07A5, 1}, - { 0x07B1, 0, 0}, - { 0x07C0, 0x07EA, 1}, - { 0x07F4, 0x07F5, 1}, - { 0x07FA, 0, 0}, - { 0x0901, 0x0939, 1}, - { 0x093C, 0x094D, 1}, - { 0x0950, 0x0954, 1}, - { 0x0958, 0x0961, 1}, - { 0x0962, 0, 0}, - { 0x0963, 0, 0}, - { 0x0972, 0, 0}, - { 0x097B, 0x097F, 1}, - { 0x0981, 0x0983, 1}, - { 0x0985, 0x098C, 1}, - { 0x098F, 0, 0}, - { 0x0990, 0, 0}, - { 0x0993, 0x09A8, 1}, - { 0x09AA, 0x09B0, 1}, - { 0x09B2, 0, 0}, - { 0x09B6, 0x09B9, 1}, - { 0x09BC, 0x09C4, 1}, - { 0x09C7, 0, 0}, - { 0x09C8, 0, 0}, - { 0x09CB, 0x09CE, 1}, - { 0x09D7, 0, 0}, - { 0x09DC, 0, 0}, - { 0x09DD, 0, 0}, - { 0x09DF, 0x09E3, 1}, - { 0x09F0, 0x09FA, 1}, - { 0x0A01, 0x0A03, 1}, - { 0x0A05, 0x0A0A, 1}, - { 0x0A0F, 0, 0}, - { 0x0A10, 0, 0}, - { 0x0A13, 0x0A28, 1}, - { 0x0A2A, 0x0A30, 1}, - { 0x0A32, 0, 0}, - { 0x0A33, 0, 0}, - { 0x0A35, 0, 0}, - { 0x0A36, 0, 0}, - { 0x0A38, 0, 0}, - { 0x0A39, 0, 0}, - { 0x0A3C, 0, 0}, - { 0x0A3E, 0x0A42, 1}, - { 0x0A47, 0, 0}, - { 0x0A48, 0, 0}, - { 0x0A4B, 0x0A4D, 1}, - { 0x0A51, 0, 0}, - { 0x0A59, 0x0A5C, 1}, - { 0x0A5E, 0, 0}, - { 0x0A70, 0x0A75, 1}, - { 0x0A81, 0x0A83, 1}, - { 0x0A85, 0x0A8D, 1}, - { 0x0A8F, 0x0A91, 1}, - { 0x0A93, 0x0AA8, 1}, - { 0x0AAA, 0x0AB0, 1}, - { 0x0AB2, 0, 0}, - { 0x0AB3, 0, 0}, - { 0x0AB5, 0x0AB9, 1}, - { 0x0ABC, 0x0AC5, 1}, - { 0x0AC7, 0x0AC9, 1}, - { 0x0ACB, 0x0ACD, 1}, - { 0x0AD0, 0, 0}, - { 0x0AE0, 0x0AE3, 1}, - { 0x0AF1, 0, 0}, - { 0x0B01, 0x0B03, 1}, - { 0x0B05, 0x0B0C, 1}, - { 0x0B0F, 0, 0}, - { 0x0B10, 0, 0}, - { 0x0B13, 0x0B28, 1}, - { 0x0B2A, 0x0B30, 1}, - { 0x0B32, 0, 0}, - { 0x0B33, 0, 0}, - { 0x0B35, 0x0B39, 1}, - { 0x0B3C, 0x0B44, 1}, - { 0x0B47, 0x0B48, 1}, - { 0x0B4B, 0x0B4D, 1}, - { 0x0B56, 0x0B57, 1}, - { 0x0B5C, 0, 0}, - { 0x0B5D, 0, 0}, - { 0x0B5F, 0x0B63, 1}, - { 0x0B70, 0, 0}, - { 0x0B71, 0, 0}, - { 0x0B82, 0, 0}, - { 0x0B83, 0, 0}, - { 0x0B85, 0x0B8A, 1}, - { 0x0B8E, 0x0B90, 1}, - { 0x0B92, 0x0B95, 1}, - { 0x0B99, 0, 0}, - { 0x0B9A, 0, 0}, - { 0x0B9C, 0, 0}, - { 0x0B9E, 0, 0}, - { 0x0B9F, 0, 0}, - { 0x0BA3, 0, 0}, - { 0x0BA4, 0, 0}, - { 0x0BA8, 0x0BAA, 1}, - { 0x0BAE, 0x0BB9, 1}, - { 0x0BBE, 0x0BC2, 1}, - { 0x0BC6, 0x0BC8, 1}, - { 0x0BCA, 0x0BCD, 1}, - { 0x0BD0, 0, 0}, - { 0x0BD7, 0, 0}, - { 0x0BF0, 0x0BFA, 1}, - { 0x0C01, 0x0C03, 1}, - { 0x0C05, 0x0C0C, 1}, - { 0x0C0E, 0x0C10, 1}, - { 0x0C12, 0x0C28, 1}, - { 0x0C2A, 0x0C33, 1}, - { 0x0C35, 0x0C39, 1}, - { 0x0C3D, 0x0C44, 1}, - { 0x0C46, 0x0C48, 1}, - { 0x0C4A, 0x0C4D, 1}, - { 0x0C55, 0x0C56, 1}, - { 0x0C58, 0x0C59, 1}, - { 0x0C60, 0x0C63, 1}, - { 0x0C82, 0x0C83, 1}, - { 0x0C85, 0x0C8C, 1}, - { 0x0C8E, 0x0C90, 1}, - { 0x0C92, 0x0CA8, 1}, - { 0x0CAA, 0x0CB3, 1}, - { 0x0CB5, 0x0CB9, 1}, - { 0x0CBC, 0x0CC4, 1}, - { 0x0CC6, 0x0CC8, 1}, - { 0x0CCA, 0x0CCD, 1}, - { 0x0CD5, 0x0CD6, 1}, - { 0x0CDE, 0, 0}, - { 0x0CE0, 0x0CE3, 1}, - { 0x0CF1, 0, 0}, - { 0x0CF2, 0, 0}, - { 0x0D02, 0x0D03, 1}, - { 0x0D05, 0x0D0C, 1}, - { 0x0D0E, 0x0D10, 1}, - { 0x0D12, 0x0D28, 1}, - { 0x0D2A, 0x0D39, 1}, - { 0x0D3D, 0x0D44, 1}, - { 0x0D46, 0x0D48, 1}, - { 0x0D4A, 0x0D4D, 1}, - { 0x0D57, 0, 0}, - { 0x0D60, 0x0D63, 1}, - { 0x0D79, 0x0D7F, 1}, - { 0x0D82, 0x0D83, 1}, - { 0x0D85, 0x0D96, 1}, - { 0x0D9A, 0x0DB1, 1}, - { 0x0DB3, 0x0DBB, 1}, - { 0x0DBD, 0, 0}, - { 0x0DC0, 0x0DC6, 1}, - { 0x0DCA, 0, 0}, - { 0x0DCF, 0x0DD4, 1}, - { 0x0DD6, 0, 0}, - { 0x0DD8, 0x0DDF, 1}, - { 0x0DF2, 0x0DF4, 1}, - { 0x0E01, 0x0E2E, 1}, - { 0x0E30, 0x0E3A, 1}, - { 0x0E40, 0x0E45, 1}, - { 0x0E47, 0x0E4E, 1}, - { 0x0E81, 0x0E82, 1}, - { 0x0E84, 0, 0}, - { 0x0E87, 0x0E88, 1}, - { 0x0E8A, 0, 0}, - { 0x0E8D, 0, 0}, - { 0x0E94, 0x0E97, 1}, - { 0x0E99, 0x0E9F, 1}, - { 0x0EA1, 0x0EA3, 1}, - { 0x0EA5, 0, 0}, - { 0x0EA7, 0, 0}, - { 0x0EAA, 0x0EAB, 1}, - { 0x0EAD, 0x0EB0, 1}, - { 0x0EB2, 0x0EB3, 1}, - { 0x0EBD, 0, 0}, - { 0x0EC0, 0x0EC4, 1}, - { 0x0EC6, 0, 0}, - { 0x0EDC, 0x0EDD, 1}, - { 0x0F00, 0, 0}, - { 0x0F40, 0x0F47, 1}, - { 0x0F49, 0x0F6C, 1}, - { 0x0F88, 0x0F8B, 1}, - { 0x1000, 0x102A, 1}, - { 0x1050, 0x1055, 1}, - { 0x105A, 0x105D, 1}, - { 0x1061, 0, 0}, - { 0x0165, 0, 0}, - { 0x1066, 0, 0}, - { 0x106E, 0x1070, 1}, - { 0x1075, 0x1081, 1}, - { 0x108E, 0, 0}, - { 0x10A0, 0x10C5, 1}, - { 0x10D0, 0x10FA, 1}, - { 0x10FC, 0, 0}, - { 0x1100, 0x1159, 1}, - { 0x115F, 0x11A2, 1}, - { 0x11A8, 0x11F9, 1}, - { 0x1200, 0x1248, 1}, - { 0x124A, 0x124D, 1}, - { 0x1250, 0x1256, 1}, - { 0x1258, 0, 0}, - { 0x125A, 0x125D, 1}, - { 0x1260, 0x1288, 1}, - { 0x128A, 0x128D, 1}, - { 0x1290, 0x12B0, 1}, - { 0x12B2, 0x12B5, 1}, - { 0x12B8, 0x12BE, 1}, - { 0x12C0, 0, 0}, - { 0x12C2, 0x12C5, 1}, - { 0x12C8, 0x12D6, 1}, - { 0x12D8, 0x1310, 1}, - { 0x1312, 0x1315, 1}, - { 0x1318, 0x135A, 1}, - { 0x1380, 0x138F, 1}, - { 0x13A0, 0x13F4, 1}, - { 0x1401, 0x166C, 1}, - { 0x166F, 0x1676, 1}, - { 0x1681, 0x169A, 1}, - { 0x16A0, 0x16EA, 1}, - { 0x16EE, 0x16F0, 1}, - { 0x1700, 0x170C, 1}, - { 0x170E, 0x1711, 1}, - { 0x1720, 0x1731, 1}, - { 0x1740, 0x1751, 1}, - { 0x1760, 0x176C, 1}, - { 0x176E, 0x1770, 1}, - { 0x1780, 0x17B3, 1}, - { 0x17D7, 0, 0}, - { 0x17DC, 0, 0}, - { 0x1820, 0x1877, 1}, - { 0x1880, 0x18A8, 1}, - { 0x18AA, 0, 0}, - { 0x1900, 0x191C, 1}, - { 0x1946, 0x194F, 1}, - { 0x1950, 0x196D, 1}, - { 0x1970, 0x1974, 1}, - { 0x1980, 0x19A9, 1}, - { 0x19C1, 0x19C7, 1}, - { 0x19D0, 0x19D9, 1}, - { 0x1A00, 0x1A16, 1}, - { 0x1B05, 0x1B33, 1}, - { 0x1B45, 0x1B4B, 1}, - { 0x1B50, 0x1B59, 1}, - { 0x1B83, 0x1BA0, 1}, - { 0x1BAE, 0x1BAF, 1}, - { 0x1C00, 0x1C23, 1}, - { 0x1C4D, 0x1C4F, 1}, - { 0x1C5A, 0x1C7D, 1}, - { 0x1D00, 0x1DBF, 1}, - { 0x1E00, 0x1E9F, 1}, - { 0x1EA0, 0x1EFF, 1}, - { 0x1F00, 0x1F15, 1}, - { 0x1F18, 0x1F1D, 1}, - { 0x1F20, 0x1F45, 1}, - { 0x1F48, 0x1F4D, 1}, - { 0x1F50, 0x1F57, 1}, - { 0x1F59, 0, 0}, - { 0x1F5B, 0, 0}, - { 0x1F5D, 0, 0}, - { 0x1F5F, 0x1F7D, 1}, - { 0x1F80, 0x1FB4, 1}, - { 0x1FB6, 0x1FBC, 1}, - { 0x1FBE, 0, 0}, - { 0x1FC2, 0x1FC4, 1}, - { 0x1FC6, 0x1FCC, 1}, - { 0x1FD0, 0x1FD3, 1}, - { 0x1FD6, 0x1FDB, 1}, - { 0x1FE0, 0x1FEC, 1}, - { 0x1FF2, 0x1FF4, 1}, - { 0x1FF6, 0x1FFC, 1}, - { 0x2071, 0, 0}, - { 0x207F, 0, 0}, - { 0x2090, 0x2094, 1}, - { 0x2102, 0, 0}, - { 0x2107, 0, 0}, - { 0x210A, 0x2113, 1}, - { 0x2115, 0, 0}, - { 0x2119, 0x211D, 1}, - { 0x2124, 0, 0}, - { 0x2126, 0, 0}, - { 0x2128, 0x212D, 1}, - { 0x212F, 0x2139, 1}, - { 0x213C, 0x213F, 1}, - { 0x2145, 0x2149, 1}, - { 0x214E, 0, 0}, - { 0x2160, 0x2188, 1}, - { 0x249C, 0x24E9, 1}, - { 0x2C00, 0x2C2E, 1}, - { 0x2C30, 0x2C5E, 1}, - { 0x2C60, 0x2C6F, 1}, - { 0x2C71, 0x2C7D, 1}, - { 0x2C80, 0x2CE4, 1}, - { 0x2D00, 0x2D25, 1}, - { 0x2D30, 0x2D65, 1}, - { 0x2D6F, 0, 0}, - { 0x2D80, 0x2D96, 1}, - { 0x2DA0, 0x2DA6, 1}, - { 0x2DA8, 0x2DAE, 1}, - { 0x2DB0, 0x2DB6, 1}, - { 0x2DB8, 0x2DBE, 1}, - { 0x2DC0, 0x2DC6, 1}, - { 0x2DC8, 0x2DCE, 1}, - { 0x2DD0, 0x2DD6, 1}, - { 0x2DD8, 0x2DDE, 1}, - { 0x3005, 0x3007, 1}, - { 0x3021, 0x3029, 1}, - { 0x3031, 0x3035, 1}, - { 0x3038, 0x303C, 1}, - { 0x3041, 0x3096, 1}, - { 0x309D, 0x309F, 1}, - { 0x30A1, 0x30FA, 1}, - { 0x30FC, 0x30FF, 1}, - { 0x3105, 0x312D, 1}, - { 0x3131, 0x318E, 1}, - { 0x31A0, 0x31B7, 1}, - { 0x31F0, 0x31FF, 1}, - { 0x3400, 0x4DB5, 1}, - { 0x4E00, 0x9FBB, 1}, - { 0xA000, 0xA48C, 1}, - { 0xA500, 0xA60B, 1}, - { 0xA610, 0xA61F, 1}, - { 0xA62A, 0xA62B, 1}, - { 0xA640, 0xA65F, 1}, - { 0xA662, 0xA66E, 1}, - { 0xA680, 0xA697, 1}, - { 0xA717, 0xA71F, 1}, - { 0xA722, 0xA78C, 1}, - { 0xA7FB, 0xA7FF, 1}, - { 0xA800, 0, 0}, - { 0xA801, 0, 0}, - { 0xA803, 0xA805, 1}, - { 0xA807, 0xA80A, 1}, - { 0xA80C, 0xA822, 1}, - { 0xA840, 0xA873, 1}, - { 0xA882, 0xA8B3, 1}, - { 0xA90A, 0xA92D, 1}, - { 0xA930, 0xA946, 1}, - { 0xAA00, 0xAA28, 1}, - { 0xAA40, 0xAA42, 1}, - { 0xAA44, 0xAA4B, 1}, - { 0xAC00, 0xD7A3, 1}, - { 0xF900, 0xFA2D, 1}, - { 0xFA30, 0xFA6A, 1}, - { 0xFA70, 0xFAD9, 1}, - { 0xFB00, 0xFB06, 1}, - { 0xFB13, 0xFB17, 1}, - { 0xFB1D, 0, 0}, - { 0xFB1F, 0xFB28, 1}, - { 0xFB2A, 0xFB36, 1}, - { 0xFB38, 0xFB3C, 1}, - { 0xFB3E, 0, 0}, - { 0xFB40, 0, 0}, - { 0xFB41, 0, 0}, - { 0xFB43, 0, 0}, - { 0xFB44, 0, 0}, - { 0xFB46, 0xFB4F, 1}, - { 0xFB50, 0xFBB1, 1}, - { 0xFBD3, 0xFD3D, 1}, - { 0xFD50, 0xFD8F, 1}, - { 0xFD92, 0xFDC7, 1}, - { 0xFDF0, 0xFDFB, 1}, - { 0xFE70, 0xFE74, 1}, - { 0xFE76, 0xFEFC, 1}, - { 0xFF21, 0xFF3A, 1}, - { 0xFF41, 0xFF5A, 1}, - { 0xFF66, 0xFFBE, 1}, - { 0xFFC2, 0xFFC7, 1}, - { 0xFFCA, 0xFFCF, 1}, - { 0xFFD2, 0xFFD7, 1}, - { 0xFFDA, 0xFFDC, 1}, - {0x00010000, 0x0001000B, 1}, - {0x0001000D, 0x00010026, 1}, - {0x00010028, 0x0001003A, 1}, - {0x0001003C, 0x0001003D, 1}, - {0x0001003F, 0x0001004D, 1}, - {0x00010050, 0x0001005D, 1}, - {0x00010080, 0x000100FA, 1}, - {0x00010140, 0x00010174, 1}, - {0x00010280, 0x0001029C, 1}, - {0x000102A0, 0x000102D0, 1}, - {0x00010300, 0x0001031E, 1}, - {0x00010330, 0x0001034A, 1}, - {0x00010380, 0x0001039D, 1}, - {0x000103A0, 0x000103C3, 1}, - {0x000103C8, 0x000103CF, 1}, - {0x000103D1, 0x000103D5, 1}, - {0x00010400, 0x0001044F, 1}, - {0x00010450, 0x0001047F, 1}, - {0x00010480, 0x0001049D, 1}, - {0x000104A0, 0x000104A9, 1}, - {0x00010800, 0x00010805, 1}, - {0x00010808, 0, 0}, - {0x0001080A, 0x00010835, 1}, - {0x00010837, 0x00010838, 1}, - {0x0001083C, 0, 0}, - {0x0001083F, 0, 0}, - {0x00010900, 0x00010915, 1}, - {0x00010A00, 0, 0}, - {0x00010A10, 0x00010A13, 1}, - {0x00010A15, 0x00010A17, 1}, - {0x00010A19, 0x00010A33, 1}, - {0x00012000, 0x0001236E, 1}, - {0x00012400, 0x00012462, 1}, - {0x0001D400, 0x0001D454, 1}, - {0x0001D456, 0x0001D49C, 1}, - {0x0001D49E, 0x0001D49F, 1}, - {0x0001D4A2, 0, 0}, - {0x0001D4A5, 0x0001D4A6, 1}, - {0x0001D4A9, 0x0001D4AC, 1}, - {0x0001D4AE, 0x0001D4B9, 1}, - {0x0001D4BB, 0, 0}, - {0x0001D4BD, 0x0001D4C3, 1}, - {0x0001D4C5, 0x0001D505, 1}, - {0x0001D507, 0x0001D50A, 1}, - {0x0001D50D, 0x0001D514, 1}, - {0x0001D516, 0x0001D51C, 1}, - {0x0001D51E, 0x0001D539, 1}, - {0x0001D53B, 0x0001D53E, 1}, - {0x0001D540, 0x0001D544, 1}, - {0x0001D546, 0, 0}, - {0x0001D54A, 0x0001D550, 1}, - {0x0001D552, 0x0001D6A5, 1}, - {0x0001D6A8, 0x0001D6C0, 1}, - {0x0001D6C2, 0x0001D6DA, 1}, - {0x0001D6DC, 0x0001D6FA, 1}, - {0x0001D6FC, 0x0001D714, 1}, - {0x0001D716, 0x0001D734, 1}, - {0x0001D736, 0x0001D74E, 1}, - {0x0001D750, 0x0001D76E, 1}, - {0x0001D770, 0x0001D788, 1}, - {0x0001D78A, 0x0001D7A8, 1}, - {0x0001D7AA, 0x0001D7C2, 1}, - {0x0001D7C4, 0x0001D7CB, 1}, - {0x0001D7CE, 0x0001D7FF, 1}, - {0x00020000, 0x0002A6D6, 1}, - {0x0002F800, 0x0002FA1D, 1}, - { 0x0660, 0x0669, 1}, - { 0x06F0, 0x06F9, 1}, - { 0x0966, 0x096F, 1}, - { 0x09E6, 0x09EF, 1}, - { 0x0A66, 0x0A6F, 1}, - { 0x0AE6, 0x0AEF, 1}, - { 0x0B66, 0x0B6F, 1}, - { 0x0BE6, 0x0BEF, 1}, - { 0x0C66, 0x0C6F, 1}, - { 0x0C78, 0x0C7F, 1}, - { 0x0CE6, 0x0CEF, 1}, - { 0x0D66, 0x0D75, 1}, - { 0x0D70, 0x0D75, 1}, - { 0x0E50, 0x0E59, 1}, - { 0x0ED0, 0x0ED9, 1}, - { 0x0F20, 0x0F29, 1}, - { 0x1040, 0x1049, 1}, - { 0x17E0, 0x17E9, 1}, - { 0x1810, 0x1819, 1}, - { 0x1BB0, 0x1BB9, 1}, - { 0x1C40, 0x1C49, 1}, - { 0x1C50, 0x1C59, 1}, - { 0xA620, 0xA629, 1}, - { 0xA8D0, 0xA8D9, 1}, - { 0xA900, 0xA909, 1}, - { 0xAA50, 0xAA59, 1}, - { 0xFF10, 0xFF19, 1}, - -/* END::UTF8TABLE */ -}; - -static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len) -{ - unsigned char ub = utf8_mblen[*uc]; - - if (!ub || ub > len || ub > 4) { - return 0; - } - - *wc = *uc & utf8_mask[ub]; - - switch (ub) { - case 4: - if ((uc[1] & 0xc0) != 0x80) { - return 0; - } - *wc <<= 6; - *wc += *++uc & 0x3f; - /* no break */ - case 3: - if ((uc[1] & 0xc0) != 0x80) { - return 0; - } - *wc <<= 6; - *wc += *++uc & 0x3f; - /* no break */ - case 2: - if ((uc[1] & 0xc0) != 0x80) { - return 0; - } - *wc <<= 6; - *wc += *++uc & 0x3f; - /* no break */ - case 1: - break; - - default: - return 0; - } - - return ub; -} - -static inline zend_bool isualpha(unsigned ch) -{ - unsigned i = 0, j; - - PHP_HTTP_DUFF(sizeof(utf8_ranges)/sizeof(utf8_range_t), - if (utf8_ranges[i].start == ch) { - return 1; - } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) { - if (utf8_ranges[i].step == 1) { - return 1; - } - for (j = utf8_ranges[i].start; j <= utf8_ranges[i].end; j+= utf8_ranges[i].step) { - if (ch == j) { - return 1; - } - } - return 0; - } - ++i; - ); - return 0; -} - -static inline zend_bool isualnum(unsigned ch) -{ - /* digits */ - if (ch >= 0x30 && ch <= 0x39) { - return 1; - } - return isualpha(ch); -} - -static inline size_t wctoutf16(unsigned short u16[2], unsigned wc) -{ - if (wc > 0x10ffff || (wc >= 0xd800 && wc <= 0xdfff)) { - return 0; - } - - if (wc <= 0xffff) { - u16[0] = (unsigned short) wc; - return 1; - } - - wc -= 0x10000; - u16[0] = (unsigned short) ((wc >> 10) + 0xd800); - u16[1] = (unsigned short) ((wc & 0x3ff) + 0xdc00); - return 2; -} - -static inline size_t utf16towc(unsigned *wc, unsigned short *u16_str, size_t u16_len) -{ - if (u16_len < 1) { - return 0; - } - if (u16_str[0] - 0xd800 >= 0x800) { - *wc = u16_str[0]; - return 1; - } - if (u16_len < 2 || (u16_str[0] & 0xfffffc00) != 0xd800 || (u16_str[1] & 0xfffffc00) != 0xdc00) { - return 0; - } - *wc = (u16_str[0] << 10) + u16_str[1] - 0x35fdc00; - return 2; -} - -#endif /* PHP_HTTP_UTF8_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 --git a/php_http_version.c b/php_http_version.c deleted file mode 100644 index 7adef9d..0000000 --- a/php_http_version.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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) -{ - 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) -{ - 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); - } - minor = *ptr - '0'; - if (minor >= 0 && minor <= 9) { - return php_http_version_init(v, major, minor TSRMLS_CC); - } - } - } - } - - php_error_docref(NULL TSRMLS_CC, 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) -{ - *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 --git a/php_http_version.h b/php_http_version.h deleted file mode 100644 index 40b833e..0000000 --- a/php_http_version.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - +--------------------------------------------------------------------+ - | 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 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 - */ - diff --git a/reflection2php.php b/reflection2php.php deleted file mode 100755 index 20a1f0f..0000000 --- a/reflection2php.php +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env php -getClass()) return "\\" . $c->getName() . " "; - if ($p->isArray()) return "array "; -} -function c($n, $c) { - $_=$c; - while ($c = $c->getParentClass()) { - if (array_key_exists($n, $c->getConstants())) { - return false; - } - } - $c=$_; - foreach ((array) $c->getInterfaces() as $i) { - if (array_key_exists($n, $i->getConstants()) || !c($n, $i)) { - return false; - } - } - return true; -} - -ob_start(function($s) { - // redirect any output to stderr - fwrite(STDERR, $s); - return true; -}); - -$out = STDOUT; -switch ($argc) { - default: - case 3: - $out = fopen($argv[2], "w") or die; - case 2: - $ext = $argv[1]; - break; - - case 1: - die(sprintf($out, "Usage: %s \n", $argv[0])); -} - -fprintf($out, "getConstants() as $constant => $value) { - $ns = ($nsend = strrpos($constant, "\\")) ? substr($constant, 0, $nsend++) : ""; - $cn = substr($constant, $nsend); - $constants[$ns][$cn] = $value; -} -foreach ($ext->getFunctions() as $f) { - /* @var $f ReflectionFunction */ - $ns = $f->inNamespace() ? $f->getNamespaceName() : ""; - $functions[$ns][$f->getShortName()] = $f; -} -foreach ($ext->getClasses() as $c) { - /* @var $c ReflectionClass */ - $ns = $c->inNamespace() ? $c->getNamespaceName() : ""; - $structures[$ns][$c->getShortName()] = $c; -} - -$namespaces = array_unique(array_merge( - array_keys($constants), - array_keys($functions), - array_keys($structures) -)); - -// simple sort -natsort($namespaces); - -foreach ($namespaces as $ns) { - fprintf($out, "namespace %s%s\n{\n", $ns, strlen($ns) ? " " : ""); - // - if (isset($constants[$ns])) { - ksort($constants[$ns], SORT_NATURAL); - foreach ($constants[$ns] as $cn => $value) { - fprintf($out, "\tconst %s = %s;\n", $cn, var_export($value, true)); - } - } - // - if (isset($functions[$ns])) { - ksort($functions[$ns], SORT_NATURAL); - foreach ($functions[$ns] as $fn => $f) { - /* @var $f ReflectionFunction */ - fprintf($out, "\n\tfunction %s(", $fn); - $ps = array(); - foreach ($f->getParameters() as $p) { - $p1 = sprintf("%s%s\$%s", t($p), - $p->isPassedByReference()?"&":"", trim($p->getName(), "\"")); - if ($p->isOptional()) { - if ($p->isDefaultValueAvailable()) { - $p1 .= sprintf(" = %s", - var_export($p->getDefaultValue(), true)); - } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) { - $p1 .= " = NULL"; - } elseif ($p->isArray()) { - $p1 .= " = array()"; - } - } - $ps[] = $p1; - } - fprintf($out, "%s) {\n\t}\n", implode(", ", $ps)); - } - } - // - if (isset($structures[$ns])) { - uasort($structures[$ns], function ($a, $b) { - /* @var $a ReflectionClass */ - /* @var $b ReflectionClass */ - $score = array_sum([ - -!$a->isInterface()+ - -!$a->isAbstract()+ - -!$a->isTrait()+ - -!substr_compare($a->getShortName(), "Exception", -strlen("Exception")), - +!$b->isInterface()+ - +!$b->isAbstract()+ - +!$b->isTrait()+ - -!substr_compare($b->getShortName(), "Exception", -strlen("Exception")), - ]); - - if ($score) { - return -$score; - } - return strnatcmp($a->getShortName(), $b->getShortName()); - }); - foreach ($structures[$ns] as $cn => $c) { - fprintf($out, "\n\t%s%s %s ", m($c->getModifiers()), - $c->isInterface() ? "interface":"class", $c->getShortName()); - if ($p = $c->getParentClass()) { - fprintf($out, "extends \\%s ", $p->getName()); - } - if ($i = $c->getInterfaceNames()) { - fprintf($out, "implements \\%s ", - implode(", \\", array_filter($i, function($v) { - return $v != "Traversable"; - - })) - ); - } - fprintf($out, "\n\t{\n"); - - $_=0; - foreach ($c->getConstants() as $n => $v) { - c($n, $c) and $_+=fprintf($out, "\t\tconst %s = %s;\n", $n, - var_export($v, true)); - } - $_ and fprintf($out, "\n"); - $_=0; - foreach ($c->getProperties() as $p) { - if ($p->getDeclaringClass()->getName() == $c->getName()) { - $_+=fprintf($out, "\t\t%s\$%s;\n", m($p->getModifiers()), - $p->getName()); - } - } - $_ and fprintf($out, "\n"); - - foreach ($c->getMethods() as $m) { - if ($m->getDeclaringClass()->getName() == $c->getName()) { - fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers()), - $m->getName()); - $ps = array(); - foreach ($m->getParameters() as $p) { - $p1 = sprintf("%s%s\$%s", t($p), - $p->isPassedByReference()?"&":"", $p->getName()); - if ($p->isOptional()) { - if ($p->isDefaultValueAvailable()) { - $p1 .= sprintf(" = %s", - var_export($p->getDefaultValue(), true)); - } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) { - $p1 .= sprintf(" = NULL"); - } elseif ($p->isArray()) { - $p1 .= " = array()"; - } - } - $ps[] = $p1; - } - fprintf($out, "%s)", implode(", ", $ps)); - if ($m->isAbstract()) { - fprintf($out, ";\n\n"); - } else { - fprintf($out, " {\n\t\t}\n\n"); - } - } - } - - fprintf($out, "\t}\n"); - - } - } - // - fprintf($out, "}\n\n"); -} diff --git a/scripts/bench_select_vs_event.php b/scripts/bench_select_vs_event.php new file mode 100644 index 0000000..3bc9aed --- /dev/null +++ b/scripts/bench_select_vs_event.php @@ -0,0 +1,53 @@ + -n -c [-p (enable pipelining)] [-e (use libevent)]\n", $argv[0]); + fprintf(STDERR, "\nDefaults: -u http://localhost/ -n 1000 -c 10\n\n"); + exit(-1); +} + +function push($client, $url, &$n) { + if ($n-- > 0) { + $req = new http\Client\Request("GET", $url); + $client->enqueue($req, function($response) use ($client, $req, $url, &$n) { + global $count; ++$count; + push($client, $url, $n); + return true; // dequeue + }); + } +} + +isset($argv) or $argv = $_SERVER['argv']; +defined('STDERR') or define('STDERR', fopen('php://stderr', 'w')); + +$opts = getopt("u:c:n:e"); +isset($opts["u"]) or $opts["u"] = "http://localhost/"; +isset($opts["c"]) or $opts["c"] = 10; +isset($opts["n"]) or $opts["n"] = 1000; + +$argc > 1 or usage(); + +$time = microtime(true); +$count = 0; +$client = new http\Client; + +$client->enablePipelining($opts["p"]===false); +$client->enableEvents($opts["e"]===false); + +for ($i = 0, $x = $opts["n"]; $i < $opts["c"]; ++$i) { + push($client, $opts["u"], $x); +} + +try { + $client->send(); +} catch (Exception $e) { + echo $e; +} + +printf("\n> %10.6fs (%3.2fM)\n", microtime(true)-$time, memory_get_peak_usage(true)/1024/1024); + +$count == $opts["n"] or printf("\nOnly %d finished\n", $count); diff --git a/scripts/check_package-xml.php b/scripts/check_package-xml.php new file mode 100755 index 0000000..e422c6a --- /dev/null +++ b/scripts/check_package-xml.php @@ -0,0 +1,122 @@ +#!/usr/bin/env php + 1) { + if ($argv[1] === "-") { + $file = "php://stdin"; + } else { + $file = $argv[1]; + } +} elseif (stdin_is_readable()) { + $file = "php://stdin"; +} else { + $file = "./package.xml"; +} + +if (($xml = simplexml_load_file($file))) { + $xml_files = xmllist($xml->contents[0]); + $dirs = ["."]; + while ($dir = array_shift($dirs)) { + foreach (dirlist($dir) as $file) { + if (is_gitignored($file)) { + continue; + } + if (is_commonly_ignored($file)) { + continue; + } + if (!is_dir($file)) { + if (!in_array($file, $xml_files)) { + echo "Missing file $file\n"; + } + } else { + $base = basename($file); + if ($base{0} !== ".") { + array_push($dirs, $file); + } + } + } + } + foreach ($xml_files as $file) { + if (!file_exists($file)) { + echo "Extraneous file $file\n"; + } + } +} + +### + +function error($fmt) { + trigger_error(call_user_func_array("sprintf", func_get_args())); +} + +function stdin_is_readable() { + $r = [STDIN]; $w = $e = []; + return stream_select($r, $w, $e, 0); +} + +function is_gitignored($file) { + static $gitignore, $gitmodules; + + if (!isset($gitmodules)) { + if (is_readable("./.gitmodules")) { + $gitmodules = explode("\n", `git submodule status | awk '{printf$2}'`); + } else { + $gitmodules = false; + } + } + if (!isset($gitignore)) { + if (is_readable("./.gitignore")) { + $ignore_submodules = $gitmodules ? " ! -path './".implode("/*' ! -path './", $gitmodules)."/*'" : ""; + $gitignore = explode("\n", `find . $ignore_submodules | git check-ignore --stdin`); + } else { + $gitignore = false; + } + } + if ($gitignore) { + if (in_array($file, $gitignore)) { + return true; + } + } + if ($gitmodules) { + foreach ($gitmodules as $module) { + if (fnmatch("./$module/*", $file)) { + return true; + } + } + } + return false; +} + +function is_commonly_ignored($file) { + return fnmatch("./.git*", $file) + || in_array($file, ["./package.xml", "./package2.xml", "./.travis.yml", "./.editorconfig"], true); +} + +function xmllist(SimpleXmlElement $dir, $p = ".", &$a = null) { + settype($a, "array"); + $p = trim($p, "/") . "/" . trim($dir["name"], "/") . "/"; + foreach ($dir as $file) { + switch ($file->getName()) { + case "dir": + xmllist($file, $p, $a); + break; + case "file": + $a[] = sprintf("%s/%s", trim($p, "/"), trim($file["name"])); + break; + default: + error("Unknown content type: %s", $file->getName()); + break; + } + } + return $a; +} + +function dirlist($dir, $p = null) { + $p = implode("/", array_filter([trim($p, "/"), trim($dir, "/")])); + foreach (scandir($p) as $file) { + yield $p."/".$file; + } +} diff --git a/scripts/gen_curlinfo.php b/scripts/gen_curlinfo.php new file mode 100755 index 0000000..076a10d --- /dev/null +++ b/scripts/gen_curlinfo.php @@ -0,0 +1,99 @@ +#!/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); + } +', +'DOUBLE' => +' if (CURLE_OK == curl_easy_getinfo(ch, %s, &d)) { + add_assoc_double_ex(&array, "%s", sizeof("%2$s"), d); + } +', +'LONG' => +' if (CURLE_OK == curl_easy_getinfo(ch, %s, &l)) { + add_assoc_long_ex(&array, "%s", sizeof("%2$s"), l); + } +', +'SLIST' => +' if (CURLE_OK == curl_easy_getinfo(ch, %s, &s)) { + MAKE_STD_ZVAL(subarray); + array_init(subarray); + for (p = s; p; p = p->next) { + if (p->data) { + add_next_index_string(subarray, p->data, 1); + } + } + add_assoc_zval_ex(&array, "%s", sizeof("%2$s"), subarray); + 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 --git a/scripts/gen_stubs.php b/scripts/gen_stubs.php new file mode 100755 index 0000000..20a1f0f --- /dev/null +++ b/scripts/gen_stubs.php @@ -0,0 +1,205 @@ +#!/usr/bin/env php +getClass()) return "\\" . $c->getName() . " "; + if ($p->isArray()) return "array "; +} +function c($n, $c) { + $_=$c; + while ($c = $c->getParentClass()) { + if (array_key_exists($n, $c->getConstants())) { + return false; + } + } + $c=$_; + foreach ((array) $c->getInterfaces() as $i) { + if (array_key_exists($n, $i->getConstants()) || !c($n, $i)) { + return false; + } + } + return true; +} + +ob_start(function($s) { + // redirect any output to stderr + fwrite(STDERR, $s); + return true; +}); + +$out = STDOUT; +switch ($argc) { + default: + case 3: + $out = fopen($argv[2], "w") or die; + case 2: + $ext = $argv[1]; + break; + + case 1: + die(sprintf($out, "Usage: %s \n", $argv[0])); +} + +fprintf($out, "getConstants() as $constant => $value) { + $ns = ($nsend = strrpos($constant, "\\")) ? substr($constant, 0, $nsend++) : ""; + $cn = substr($constant, $nsend); + $constants[$ns][$cn] = $value; +} +foreach ($ext->getFunctions() as $f) { + /* @var $f ReflectionFunction */ + $ns = $f->inNamespace() ? $f->getNamespaceName() : ""; + $functions[$ns][$f->getShortName()] = $f; +} +foreach ($ext->getClasses() as $c) { + /* @var $c ReflectionClass */ + $ns = $c->inNamespace() ? $c->getNamespaceName() : ""; + $structures[$ns][$c->getShortName()] = $c; +} + +$namespaces = array_unique(array_merge( + array_keys($constants), + array_keys($functions), + array_keys($structures) +)); + +// simple sort +natsort($namespaces); + +foreach ($namespaces as $ns) { + fprintf($out, "namespace %s%s\n{\n", $ns, strlen($ns) ? " " : ""); + // + if (isset($constants[$ns])) { + ksort($constants[$ns], SORT_NATURAL); + foreach ($constants[$ns] as $cn => $value) { + fprintf($out, "\tconst %s = %s;\n", $cn, var_export($value, true)); + } + } + // + if (isset($functions[$ns])) { + ksort($functions[$ns], SORT_NATURAL); + foreach ($functions[$ns] as $fn => $f) { + /* @var $f ReflectionFunction */ + fprintf($out, "\n\tfunction %s(", $fn); + $ps = array(); + foreach ($f->getParameters() as $p) { + $p1 = sprintf("%s%s\$%s", t($p), + $p->isPassedByReference()?"&":"", trim($p->getName(), "\"")); + if ($p->isOptional()) { + if ($p->isDefaultValueAvailable()) { + $p1 .= sprintf(" = %s", + var_export($p->getDefaultValue(), true)); + } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) { + $p1 .= " = NULL"; + } elseif ($p->isArray()) { + $p1 .= " = array()"; + } + } + $ps[] = $p1; + } + fprintf($out, "%s) {\n\t}\n", implode(", ", $ps)); + } + } + // + if (isset($structures[$ns])) { + uasort($structures[$ns], function ($a, $b) { + /* @var $a ReflectionClass */ + /* @var $b ReflectionClass */ + $score = array_sum([ + -!$a->isInterface()+ + -!$a->isAbstract()+ + -!$a->isTrait()+ + -!substr_compare($a->getShortName(), "Exception", -strlen("Exception")), + +!$b->isInterface()+ + +!$b->isAbstract()+ + +!$b->isTrait()+ + -!substr_compare($b->getShortName(), "Exception", -strlen("Exception")), + ]); + + if ($score) { + return -$score; + } + return strnatcmp($a->getShortName(), $b->getShortName()); + }); + foreach ($structures[$ns] as $cn => $c) { + fprintf($out, "\n\t%s%s %s ", m($c->getModifiers()), + $c->isInterface() ? "interface":"class", $c->getShortName()); + if ($p = $c->getParentClass()) { + fprintf($out, "extends \\%s ", $p->getName()); + } + if ($i = $c->getInterfaceNames()) { + fprintf($out, "implements \\%s ", + implode(", \\", array_filter($i, function($v) { + return $v != "Traversable"; + + })) + ); + } + fprintf($out, "\n\t{\n"); + + $_=0; + foreach ($c->getConstants() as $n => $v) { + c($n, $c) and $_+=fprintf($out, "\t\tconst %s = %s;\n", $n, + var_export($v, true)); + } + $_ and fprintf($out, "\n"); + $_=0; + foreach ($c->getProperties() as $p) { + if ($p->getDeclaringClass()->getName() == $c->getName()) { + $_+=fprintf($out, "\t\t%s\$%s;\n", m($p->getModifiers()), + $p->getName()); + } + } + $_ and fprintf($out, "\n"); + + foreach ($c->getMethods() as $m) { + if ($m->getDeclaringClass()->getName() == $c->getName()) { + fprintf($out, "\t\t%sfunction %s(", m($m->getModifiers()), + $m->getName()); + $ps = array(); + foreach ($m->getParameters() as $p) { + $p1 = sprintf("%s%s\$%s", t($p), + $p->isPassedByReference()?"&":"", $p->getName()); + if ($p->isOptional()) { + if ($p->isDefaultValueAvailable()) { + $p1 .= sprintf(" = %s", + var_export($p->getDefaultValue(), true)); + } elseif (!($p->isArray() || $p->getClass()) || $p->allowsNull()) { + $p1 .= sprintf(" = NULL"); + } elseif ($p->isArray()) { + $p1 .= " = array()"; + } + } + $ps[] = $p1; + } + fprintf($out, "%s)", implode(", ", $ps)); + if ($m->isAbstract()) { + fprintf($out, ";\n\n"); + } else { + fprintf($out, " {\n\t\t}\n\n"); + } + } + } + + fprintf($out, "\t}\n"); + + } + } + // + fprintf($out, "}\n\n"); +} diff --git a/scripts/gen_travis_yml.php b/scripts/gen_travis_yml.php new file mode 100755 index 0000000..89ab0b2 --- /dev/null +++ b/scripts/gen_travis_yml.php @@ -0,0 +1,45 @@ +#!/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"], + "enable_debug", + "enable_maintainer_zts", + "enable_json", + "enable_hash" => ["yes"], + "enable_iconv" => ["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 + +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 --git a/scripts/gen_utf8.php b/scripts/gen_utf8.php new file mode 100755 index 0000000..865a2f5 --- /dev/null +++ b/scripts/gen_utf8.php @@ -0,0 +1,90 @@ +#!/usr/bin/env php += 2 ? $argv[1] : "/usr/share/i18n/locales/i18n"; + +$f = fopen($i18n, "r"); +$c = false; +$a = false; + +ob_start(null, 0xffff); +while (!feof($f)) { + $line = fgets($f); + if (!$c && $line !== "LC_CTYPE\n") { + continue; + } + $c = true; + if ($line === "END LC_CTYPE\n") { + break; + } + switch($line{0}) { + case "%": + break; + case "\n": + if ($a) { + break 2; + } + break; + case " ": + if ($a) { + foreach (explode(";", trim($line, "\n/ ;")) as $ranges) { + $range = explode("..", $ranges); + $step = 0; + $end = 0; + switch (count($range)) { + case 3: + list($sstart, $sstep, $send) = $range; + sscanf($sstart, "", $start); + sscanf($sstep, "(%d)", $step); + sscanf($send, "", $end); + + break; + case 2: + list($sstart, $send) = $range; + $step = 1; + sscanf($sstart, "", $start); + sscanf($send, "", $end); + break; + case 1: + list($sstart) = $range; + sscanf($sstart, "", $start); + break; + } + print "\t{"; + if ($start >= 0xffff) { + printf("0x%08X, ", $start); + if ($end) { + printf("0x%08X, ", $end); + } else { + print(" 0, "); + } + } else { + printf(" 0x%04X, ", $start); + if ($end) { + printf(" 0x%04X, ", $end); + } else { + print(" 0, "); + } + } + printf("%d},\n", $step); + } + } + break; + default: + if ($a) { + break 2; + } elseif ($line === "alpha /\n") { + $a = true; + } + break; + } +} + +file_put_contents("php_http_utf8.h", + preg_replace('/(\/\* BEGIN::UTF8TABLE \*\/\n).*(\n\s*\/\* END::UTF8TABLE \*\/)/s', '$1'. ob_get_contents() .'$2', + file_get_contents("php_http_utf8.h"))); diff --git a/src/php_http.c b/src/php_http.c new file mode 100644 index 0000000..2ff20f1 --- /dev/null +++ b/src/php_http.c @@ -0,0 +1,252 @@ +/* + +--------------------------------------------------------------------+ + | 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_free(zend_php_http_globals *G TSRMLS_DC) +{ +} +#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_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 --git a/src/php_http_api.h b/src/php_http_api.h new file mode 100644 index 0000000..08b6ba8 --- /dev/null +++ b/src/php_http_api.h @@ -0,0 +1,141 @@ +/* + +--------------------------------------------------------------------+ + | 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; +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)]) +# undef TSRMLS_FETCH_FROM_CTX +# define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = ((ctx)?(ctx):ts_resource_ex(0, NULL)) +#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 --git a/src/php_http_buffer.c b/src/php_http_buffer.c new file mode 100644 index 0000000..e24a4e1 --- /dev/null +++ b/src/php_http_buffer.c @@ -0,0 +1,459 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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->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) +{ + if ((buf = php_http_buffer_init(buf))) { + if (PHP_HTTP_BUFFER_NOMEM == php_http_buffer_append(buf, string, length)) { + 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) +{ + 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); + } 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) +{ + 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) +{ + 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, ...) +{ + 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) +{ + 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) +{ + 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); + 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) +{ + 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_t *storage; + + *chunk = NULL; + + if (!*s) { + *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) +{ + 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)) { + 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) +{ + size_t passed_on = 0, passed_in = php_http_buffer_chunked_input(s, chunk_size, passin, passin_arg TSRMLS_CC); + + 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); + + 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_t *str; + size_t passed; + + if (!*s) { + *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); + + 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) +{ + 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) +{ + 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) +{ + 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, ...) +{ + 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) +{ + 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, ...) +{ + 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) +{ + 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)); + 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) +{ + 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) +{ + 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, ...) +{ + 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 --git a/src/php_http_buffer.h b/src/php_http_buffer.h new file mode 100644 index 0000000..faf8992 --- /dev/null +++ b/src/php_http_buffer.h @@ -0,0 +1,250 @@ +/* + +--------------------------------------------------------------------+ + | 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; \ + 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); + +/* 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 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)) +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); + +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); + +/* 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); + +/* 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); + + +# 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 --git a/src/php_http_client.c b/src/php_http_client.c new file mode 100644 index 0000000..160e8bb --- /dev/null +++ b/src/php_http_client.c @@ -0,0 +1,1284 @@ +/* + +--------------------------------------------------------------------+ + | 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; + +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); +} + +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 *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; + } + return FAILURE; +} + +static int apply_driver_list(void *p, void *arg TSRMLS_DC) +{ + php_http_client_driver_t *d = p; + zval *zname; + + MAKE_STD_ZVAL(zname); + ZVAL_STRINGL(zname, d->name_str, d->name_len, 1); + + zend_hash_next_index_insert(arg, &zname, sizeof(zval *), NULL); + return ZEND_HASH_APPLY_KEEP; +} + +void php_http_client_driver_list(HashTable *ht TSRMLS_DC) +{ + zend_hash_apply_with_argument(&php_http_client_drivers, apply_driver_list, ht TSRMLS_CC); +} + +void php_http_client_options_set_subr(zval *this_ptr, char *key, size_t len, zval *opts, int overwrite TSRMLS_DC) +{ + 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; + + MAKE_STD_ZVAL(new_opts); + array_init(new_opts); + old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(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); + } else { + zend_symtable_del(Z_ARRVAL_P(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); + } else { + Z_ADDREF_P(opts); + zend_symtable_update(Z_ARRVAL_P(new_opts), key, len, (void *) &opts, sizeof(zval *), NULL); + } + } + + zend_update_property(this_ce, getThis(), ZEND_STRL("options"), new_opts TSRMLS_CC); + zval_ptr_dtor(&new_opts); + } +} + +void php_http_client_options_set(zval *this_ptr, zval *opts TSRMLS_DC) +{ + 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); + + MAKE_STD_ZVAL(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); + zval_ptr_dtor(&new_opts); + } else { + zval *old_opts, *add_opts, **opt; + + MAKE_STD_ZVAL(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); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { + zend_symtable_del(Z_ARRVAL_P(old_opts), key.str, key.len); + } + } else { + Z_ADDREF_P(*opt); + add_assoc_zval_ex(add_opts, key.str, key.len, *opt); + } + } + } + + old_opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); + if (Z_TYPE_P(old_opts) == IS_ARRAY) { + array_copy(Z_ARRVAL_P(old_opts), Z_ARRVAL_P(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); + 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) +{ + zend_class_entry *this_ce = Z_OBJCE_P(getThis()); + zval **options, *opts = zend_read_property(this_ce, getThis(), ZEND_STRL("options"), 0 TSRMLS_CC); + + if ((Z_TYPE_P(opts) == IS_ARRAY) && (SUCCESS == zend_symtable_find(Z_ARRVAL_P(opts), key, len, (void *) &options))) { + 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 *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); + } + } + } + + 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"); + 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"); + 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) +{ + php_http_client_object_t *o = (php_http_client_object_t *) object; + + 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_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 *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->client = client; + + if (ptr) { + *ptr = o; + } + + 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; +} + +zend_object_value php_http_client_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return php_http_client_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +static void handle_history(zval *zclient, php_http_message_t *request, php_http_message_t *response TSRMLS_DC) +{ + 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); + + MAKE_STD_ZVAL(new_hist); + ZVAL_OBJVAL(new_hist, ov, 0); + + if (Z_TYPE_P(old_hist) == IS_OBJECT) { + php_http_message_object_prepend(new_hist, old_hist, 1 TSRMLS_CC); + } + + zend_update_property(php_http_client_class_entry, zclient, ZEND_STRL("history"), new_hist TSRMLS_CC); + 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); + + if ((msg = *response)) { + php_http_message_object_t *msg_obj; + zval *info, *zresponse, *zrequest; + 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); + } + + /* 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); + + php_http_message_object_prepend(zresponse, zrequest, 1 TSRMLS_CC); + + MAKE_STD_ZVAL(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); + zval_ptr_dtor(&info); + + zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); + zend_llist_add_element(&client->responses, &msg_obj); + + if (e->closure.fci.size) { + zval *retval = NULL; + 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); + + if (retval) { + if (Z_TYPE_P(retval) == IS_BOOL) { + dequeue = Z_BVAL_P(retval); + } + 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]; + 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_ptr_dtor(&zclient); + zval_ptr_dtor(&zrequest); + zval_ptr_dtor(&zprogress); +} + +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_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; + php_resource_factory_t *rf = NULL; + php_http_client_object_t *obj; + 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); + + 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); + 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); + zval_ptr_dtor(&os); + + if (persistent_handle_len) { + char *name_str; + size_t name_len; + 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))) { + rf = php_persistent_handle_resource_factory_init(NULL, pf); + } + + efree(name_str); + } + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return); + + php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC); + + 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->iterator = 0; + php_http_client_reset(obj->client); + + RETVAL_ZVAL(getThis(), 1, 0); +} + +static HashTable *combined_options(zval *client, zval *request TSRMLS_DC) +{ + 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); + + 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)); + 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); + } + 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_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); + } + } +} + +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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + msg_obj = zend_object_store_get_object(request TSRMLS_CC); + + 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.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); + } + } + + zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + msg_obj = zend_object_store_get_object(request TSRMLS_CC); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + msg_obj = zend_object_store_get_object(request TSRMLS_CC); + + 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.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); + } + } + + zend_objects_store_add_ref_by_handle(msg_obj->zv.handle TSRMLS_CC); + + 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; + + 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); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + 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); + } + } + + /* 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); + 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; + + 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); + 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); + + 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); + + 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)) { + struct timeval timeout_val; + php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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 == 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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]; + int argc; +}; + +static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC) +{ + zval **observer = NULL; + 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); + } + return FAILURE; +} + +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; + 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); + + 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); + + 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(); + arg.argc = 1; + + if (request) { + Z_ADDREF_P(request); + arg.args[1] = &request; + arg.argc += 1; + } + + if (zprogress) { + Z_ADDREF_P(zprogress); + arg.args[2] = &zprogress; + arg.argc += 1; + } + + spl_iterator_apply(observers, notify, &arg TSRMLS_CC); + + zval_ptr_dtor(&getThis()); + if (request) { + zval_ptr_dtor(&request); + } + if (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; + 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); + + 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); + + 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); + } + + zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer); + if (retval) { + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return); + + observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC); + + 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); + } + + 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; + + 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); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + req_obj = zend_object_store_get_object(request TSRMLS_CC); + + 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_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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + req_obj = zend_object_store_get_object(request TSRMLS_CC); + + 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_client_options_set(getThis(), opts TSRMLS_CC); + + 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); + 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); + + 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); + + 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); + } +} + +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_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 1 TSRMLS_CC); + + 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_client_options_set_subr(getThis(), ZEND_STRS("cookies"), opts, 0 TSRMLS_CC); + + 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); + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_getAvailableDrivers, 0, 0, 0) +ZEND_END_ARG_INFO(); +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); + } +} + +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); + + 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); + + 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->create_object = php_http_client_object_new; + zend_class_implements(php_http_client_class_entry TSRMLS_CC, 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.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_hash_init(&php_http_client_drivers, 2, NULL, NULL, 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 --git a/src/php_http_client.h b/src/php_http_client.h new file mode 100644 index 0000000..f4a5b59 --- /dev/null +++ b/src/php_http_client.h @@ -0,0 +1,161 @@ +/* + +--------------------------------------------------------------------+ + | 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; + 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); + +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; +} 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_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 --git a/src/php_http_client_curl.c b/src/php_http_client_curl.c new file mode 100644 index 0000000..fed92fc --- /dev/null +++ b/src/php_http_client_curl.c @@ -0,0 +1,2599 @@ +/* + +--------------------------------------------------------------------+ + | 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; + + } 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) +{ + 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) +{ + 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) +{ + 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) +{ + return curl_multi_init(); +} + +static void php_http_curlm_dtor(void *opaque, void *handle TSRMLS_DC) +{ + 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) { + 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); + + /* 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); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &l)) { + add_assoc_long_ex(&array, "response_code", sizeof("response_code"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TOTAL_TIME, &d)) { + add_assoc_double_ex(&array, "total_time", sizeof("total_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NAMELOOKUP_TIME, &d)) { + add_assoc_double_ex(&array, "namelookup_time", sizeof("namelookup_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONNECT_TIME, &d)) { + add_assoc_double_ex(&array, "connect_time", sizeof("connect_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PRETRANSFER_TIME, &d)) { + add_assoc_double_ex(&array, "pretransfer_time", sizeof("pretransfer_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_UPLOAD, &d)) { + add_assoc_double_ex(&array, "size_upload", sizeof("size_upload"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SIZE_DOWNLOAD, &d)) { + add_assoc_double_ex(&array, "size_download", sizeof("size_download"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_DOWNLOAD, &d)) { + add_assoc_double_ex(&array, "speed_download", sizeof("speed_download"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SPEED_UPLOAD, &d)) { + add_assoc_double_ex(&array, "speed_upload", sizeof("speed_upload"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HEADER_SIZE, &l)) { + add_assoc_long_ex(&array, "header_size", sizeof("header_size"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REQUEST_SIZE, &l)) { + add_assoc_long_ex(&array, "request_size", sizeof("request_size"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_VERIFYRESULT, &l)) { + add_assoc_long_ex(&array, "ssl_verifyresult", sizeof("ssl_verifyresult"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_FILETIME, &l)) { + add_assoc_long_ex(&array, "filetime", sizeof("filetime"), l); + } + 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); + } + 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); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_STARTTRANSFER_TIME, &d)) { + add_assoc_double_ex(&array, "starttransfer_time", sizeof("starttransfer_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_CONTENT_TYPE, &c)) { + add_assoc_string_ex(&array, "content_type", sizeof("content_type"), c ? c : "", 1); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_TIME, &d)) { + add_assoc_double_ex(&array, "redirect_time", sizeof("redirect_time"), d); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_REDIRECT_COUNT, &l)) { + add_assoc_long_ex(&array, "redirect_count", sizeof("redirect_count"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTP_CONNECTCODE, &l)) { + add_assoc_long_ex(&array, "connect_code", sizeof("connect_code"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_HTTPAUTH_AVAIL, &l)) { + add_assoc_long_ex(&array, "httpauth_avail", sizeof("httpauth_avail"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_PROXYAUTH_AVAIL, &l)) { + add_assoc_long_ex(&array, "proxyauth_avail", sizeof("proxyauth_avail"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_OS_ERRNO, &l)) { + add_assoc_long_ex(&array, "os_errno", sizeof("os_errno"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_NUM_CONNECTS, &l)) { + add_assoc_long_ex(&array, "num_connects", sizeof("num_connects"), l); + } + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_SSL_ENGINES, &s)) { + MAKE_STD_ZVAL(subarray); + array_init(subarray); + for (p = s; p; p = p->next) { + if (p->data) { + add_next_index_string(subarray, p->data, 1); + } + } + add_assoc_zval_ex(&array, "ssl_engines", sizeof("ssl_engines"), subarray); + 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); + } +#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); + } +#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); + } +#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); + } +#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); + } +#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); + } +#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); + } +#endif + + /* END::CURLINFO */ + +#if PHP_HTTP_CURL_VERSION(7,34,0) + { + zval *ti_array; + struct curl_tlssessioninfo *ti; + + if (CURLE_OK == curl_easy_getinfo(ch, CURLINFO_TLS_SESSION, &ti)) { + const char *backend; + + MAKE_STD_ZVAL(subarray); + ZVAL_NULL(subarray); + MAKE_STD_ZVAL(ti_array); + 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)); + } +#endif + break; + case CURLSSLBACKEND_GNUTLS: + backend = "gnutls"; +#ifdef PHP_HTTP_HAVE_GNUTLS + { + gnutls_session_t sess = ti->internals; + char *desc; + + array_init(subarray); + if ((desc = gnutls_session_get_desc(sess))) { + add_assoc_string_ex(subarray, ZEND_STRS("desc"), desc, 1); + gnutls_free(desc); + } + add_assoc_bool_ex(subarray, ZEND_STRS("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); + } + } +#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; + 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); + + for (i = 0; i < ci->num_of_certs; ++i) { + s = ci->certinfo[i]; + + MAKE_STD_ZVAL(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); + efree(keyname); + } else { + add_next_index_string(subarray, p->data, 1); + } + } + } + add_next_index_zval(ci_array, subarray); + } + add_assoc_zval_ex(&array, "certinfo", sizeof("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); + } + + 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) +{ + 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); + 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("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("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-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")); + } + 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); + + 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)); + 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_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_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); + 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)) { + 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)) { + 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) { + 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)) { + 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; + + FOREACH_KEYVAL(pos, val, cookie_key, cookie_val) { + zval *zv = php_http_ztyp(IS_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); + + zval_ptr_dtor(&zv); + } + + 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); + 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))) { + 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)) { + 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)) { + 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_ptr_dtor(&rbl); + zval_ptr_dtor(&rel); + } + + } + } + } + + 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; + + switch (php_http_array_list(Z_ARRVAL_P(val) TSRMLS_CC, 2, &z_port_start, &z_port_end)) { + 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; + /* 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); + } + 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_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)); + 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); + } + } + 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; + + 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); + } + + 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) +{ + 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); +#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); + + /* 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); +#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))) { + 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, + "PECL_HTTP/" PHP_PECL_HTTP_VERSION " " + "PHP/" PHP_VERSION " " + "libcurl/" LIBCURL_VERSION + , 0); + } + + /* 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))) { + 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))) { + 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); + /* 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); +#if PHP_HTTP_CURL_VERSION(7,25,0) + 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); + } + 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); + } + 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))) { + ZVAL_BOOL(&opt->defval, 1); + } + 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); +#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); +#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); +#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))) { + ZVAL_BOOL(&opt->defval, 1); + } + 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); +#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); + } + 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: + if (opt->setter) { + rv = opt->setter(opt, val, curl); + } else if (CURLE_OK != curl_easy_setopt(ch, opt->option, (long) Z_BVAL_P(val))) { + 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 ((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)) { + 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)); + } + 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; + 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); + } + } + + 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)); +} +#endif + +static void php_http_curlm_options_init(php_http_options_t *registry TSRMLS_DC) +{ + 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); + /* 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))) { + 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); + } + + 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)))) { + 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); + } + + if (rv != SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not set option %s (%s)", opt->name.s, 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"); + 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); + 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"); + 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_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); +#endif + + php_http_buffer_init(&header); + FOREACH_HASH_KEYVAL(pos, &msg->hdrs, header_key, header_val) { + if (header_key.type == HASH_KEY_IS_STRING) { +#if !PHP_HTTP_CURL_VERSION(7,23,0) + /* avoid duplicate content-length header */ + 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)); + 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); + } + } + 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"); + 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_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 (handler->options.headers) { + curl_slist_free_all(handler->options.headers); + handler->options.headers = 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"); + 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); + + 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) +{ + 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"); + return NULL; + } + + /* only if the client itself is setup for persistence */ + if (php_resource_factory_is_persistent(h->rf)) { + char *id_str = NULL; + size_t id_len; + int port = url->port ? url->port : 80; + zval **zport; + + if (SUCCESS == zend_hash_find(enqueue->options, ZEND_STRS("port"), (void *) &zport)) { + zval *zcpy = php_http_ztyp(IS_LONG, *zport); + + if (Z_LVAL_P(zcpy)) { + port = Z_LVAL_P(zcpy); + } + 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); + } + + 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); + 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)); + 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)); + } + + 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()"); + 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()); +#else + php_error_docref(NULL TSRMLS_CC, 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) +{ + php_http_option_t *opt = pDest; + HashTable *ht; + 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)); + } else { + /* catch deliberate NULL options */ + if (Z_TYPE(opt->defval) == IS_STRING && !Z_STRVAL(opt->defval)) { + ZVAL_NULL(entry); + } else { + ZVAL_COPY_VALUE(entry, &opt->defval); + zval_copy_ctor(entry); + } + } + + if (hash_key->nKeyLength) { + zend_hash_quick_update(ht, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void *) &entry, sizeof(zval *), NULL); + } else { + zend_hash_index_update(ht, hash_key->h, (void *) &entry, sizeof(zval *), NULL); + } + + 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); + 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); + 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)) { + return FAILURE; + } + + if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl"), &php_http_curlm_resource_factory_ops, NULL, NULL TSRMLS_CC)) { + return FAILURE; + } + if (SUCCESS != php_persistent_handle_provide(ZEND_STRL("http\\Client\\Curl\\Request"), &php_http_curle_resource_factory_ops, NULL, NULL TSRMLS_CC)) { + 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); + } + 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); + } + + /* + * 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_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 --git a/src/php_http_client_curl.h b/src/php_http_client_curl.h new file mode 100644 index 0000000..c82a09c --- /dev/null +++ b/src/php_http_client_curl.h @@ -0,0 +1,31 @@ +/* + +--------------------------------------------------------------------+ + | 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 + +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 --git a/src/php_http_client_request.c b/src/php_http_client_request.c new file mode 100644 index 0000000..0e40cc5 --- /dev/null +++ b/src/php_http_client_request.c @@ -0,0 +1,314 @@ +/* + +--------------------------------------------------------------------+ + | 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); + +#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); \ + } \ + } 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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + } + + if (zbody) { + php_http_expect(SUCCESS == php_http_message_object_set_body(obj, zbody TSRMLS_CC), 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); + } + 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; + php_http_message_object_t *obj; + zval *zct; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ct_str, &ct_len), 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); + return; + } + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + 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); + 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); + if (zct) { + RETURN_ZVAL(zct, 0, 1); + } + } +} + +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; + 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_CLIENT_REQUEST_OBJECT_INIT(obj); + + 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, + 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); + + if (old_url) { + php_http_url_free(&old_url); + } + if (new_url.query != &empty[0]) { + PTR_FREE(new_url.query); + } + + 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_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); + } + } +} + +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_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, + 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); + + if (old_url) { + php_http_url_free(&old_url); + } + PTR_FREE(new_url.query); + + 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_client_options_set(getThis(), opts TSRMLS_CC); + + 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); + 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 1 TSRMLS_CC); + + 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_client_options_set_subr(getThis(), ZEND_STRS("ssl"), opts, 0 TSRMLS_CC); + + 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); + } +} + +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); + + zend_declare_property_null(php_http_client_request_class_entry, ZEND_STRL("options"), ZEND_ACC_PROTECTED TSRMLS_CC); + + 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 --git a/src/php_http_client_request.h b/src/php_http_client_request.h new file mode 100644 index 0000000..474114b --- /dev/null +++ b/src/php_http_client_request.h @@ -0,0 +1,28 @@ +/* + +--------------------------------------------------------------------+ + | 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_REQUEST_H +#define PHP_HTTP_CLIENT_REQUEST_H + +PHP_HTTP_API zend_class_entry *php_http_client_request_class_entry; +PHP_MINIT_FUNCTION(http_client_request); + +#endif /* PHP_HTTP_CLIENT_REQUEST_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 --git a/src/php_http_client_response.c b/src/php_http_client_response.c new file mode 100644 index 0000000..8d512ec --- /dev/null +++ b/src/php_http_client_response.c @@ -0,0 +1,143 @@ +/* + +--------------------------------------------------------------------+ + | 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; + zval *allowed_extras_array = NULL; + int i = 0; + char **allowed_extras = NULL; + zval *header = NULL, **entry = NULL; + HashPosition pos; + php_http_message_object_t *msg; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|la!", &flags, &allowed_extras_array)) { + return; + } + + msg = zend_object_store_get_object(getThis() TSRMLS_CC); + array_init(return_value); + + if (allowed_extras_array) { + 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); + } + } + + if ((header = php_http_message_header(msg->message, ZEND_STRL("Set-Cookie"), 0))) { + php_http_cookie_list_t *list; + + if (Z_TYPE_P(header) == IS_ARRAY) { + zval **single_header; + + FOREACH_VAL(pos, header, single_header) { + zval *data = php_http_ztyp(IS_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; + + 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_ptr_dtor(&data); + } + } 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; + + 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_ptr_dtor(&data); + } + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); + + /* 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); + + 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); + + 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 --git a/src/php_http_client_response.h b/src/php_http_client_response.h new file mode 100644 index 0000000..15d004e --- /dev/null +++ b/src/php_http_client_response.h @@ -0,0 +1,29 @@ +/* + +--------------------------------------------------------------------+ + | 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_RESPONSE_H +#define PHP_HTTP_CLIENT_RESPONSE_H + +PHP_HTTP_API zend_class_entry *php_http_client_response_class_entry; +PHP_MINIT_FUNCTION(http_client_response); + +#endif /* PHP_HTTP_CLIENT_RESPONSE_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 --git a/src/php_http_cookie.c b/src/php_http_cookie.c new file mode 100644 index 0000000..354dfa6 --- /dev/null +++ b/src/php_http_cookie.c @@ -0,0 +1,1038 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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); + + 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) +{ + zval **cookie; + if ((SUCCESS != zend_symtable_find(&list->cookies, name, name_len + 1, (void *) &cookie)) || (Z_TYPE_PP(cookie) != IS_STRING)) { + return NULL; + } + if (zcookie) { + *zcookie = *cookie; + } + return Z_STRVAL_PP(cookie); +} + +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; + + if ((SUCCESS != zend_symtable_find(&list->extras, name, name_len + 1, (void *) &extra)) || (Z_TYPE_PP(extra) != IS_STRING)) { + return NULL; + } + if (zextra) { + *zextra = *extra; + } + return Z_STRVAL_PP(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; + + 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); +} + +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; + + 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); +} + +#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) +{ + zval *arg = php_http_zsep(1, IS_STRING, val); + + if (!(flags & PHP_HTTP_COOKIE_PARSE_RAW)) { + Z_STRLEN_P(arg) = php_raw_url_decode(Z_STRVAL_P(arg), Z_STRLEN_P(arg)); + } + + if _KEY_IS("path") { + PTR_SET(list->path, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); + } else if _KEY_IS("domain") { + PTR_SET(list->domain, estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(arg))); + } else if _KEY_IS("expires") { + char *date = estrndup(Z_STRVAL_P(arg), Z_STRLEN_P(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); + } else if _KEY_IS("secure") { + list->flags |= PHP_HTTP_COOKIE_SECURE; + } else if _KEY_IS("httpOnly") { + list->flags |= PHP_HTTP_COOKIE_HTTPONLY; + } else { + /* 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); + 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); + } + 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_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; + + 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); + 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); + } + 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); + } + } + } + } + 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)); + + 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); + + 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); + + 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); +} + +php_http_cookie_list_t *php_http_cookie_list_from_struct(php_http_cookie_list_t *list, zval *strct TSRMLS_DC) +{ + 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 *)); + } + 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 (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 (SUCCESS == zend_hash_find(ht, "expires", sizeof("expires"), (void *) &tmp)) { + if (Z_TYPE_PP(tmp) == IS_LONG) { + list->expires = Z_LVAL_PP(tmp); + } else { + long lval; + + cpy = php_http_ztyp(IS_STRING, *tmp); + if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { + list->expires = lval; + } else { + list->expires = php_parse_date(Z_STRVAL_P(cpy), NULL); + } + + zval_ptr_dtor(&cpy); + } + } + 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); + } else { + long lval; + + cpy = php_http_ztyp(IS_STRING, *tmp); + if (IS_LONG == is_numeric_string(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy), &lval, NULL, 0)) { + list->max_age = lval; + } + + zval_ptr_dtor(&cpy); + } + } + 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 (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)); + } + + 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]; + + 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]); + + php_http_buffer_append(buf, enc_str[0], enc_len[0]); + php_http_buffer_appends(buf, "="); + php_http_buffer_append(buf, enc_str[1], enc_len[1]); + php_http_buffer_appends(buf, "; "); + + efree(enc_str[0]); + efree(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); + + 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); + + zval_ptr_dtor(&tmp); + } + + 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); + } + 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); + } + + 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) +{ + return php_http_cookie_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *o; + + o = ecalloc(sizeof(*o), 1); + zend_object_std_init((zend_object *) o, ce TSRMLS_CC); + object_properties_init((zend_object *) o, ce); + + 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; +} + +#define PHP_HTTP_COOKIE_OBJECT_INIT(obj) \ + do { \ + if (!obj->list) { \ + obj->list = php_http_cookie_list_init(NULL TSRMLS_CC); \ + } \ + } while(0) + +zend_object_value php_http_cookie_object_clone(zval *this_ptr TSRMLS_DC) +{ + 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_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); + + return ov; +} + +void php_http_cookie_object_free(void *object TSRMLS_DC) +{ + php_http_cookie_object_t *obj = object; + + php_http_cookie_list_free(&obj->list); + zend_object_std_dtor((zend_object *) obj TSRMLS_CC); + efree(obj); +} + +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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); + 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; + + ae = ae_ptr; + FOREACH_HASH_VAL(pos, allowed_extras, val) { + zval *cpy = php_http_ztyp(IS_STRING, *val); + + *ae_ptr++ = estrndup(Z_STRVAL_P(cpy), Z_STRLEN_P(cpy)); + zval_ptr_dtor(&cpy); + } + *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 (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); + break; + default: { + zval *cpy = php_http_ztyp(IS_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); + 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); + + 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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + array_init(return_value); + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); +} + +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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + array_init(return_value); + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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; + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { + return; + } + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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; + php_http_cookie_object_t *obj; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { + return; + } + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (obj->list->domain) { + RETURN_STRING(obj->list->domain, 1); + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + if (obj->list->path) { + RETURN_STRING(obj->list->path, 1); + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + 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; + php_http_cookie_object_t *obj; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &ts), invalid_arg, return); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + obj->list->max_age = ts; + + 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); + + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + php_http_cookie_list_to_string(obj->list, &str, &len); + + RETURN_STRINGL(str, len, 0); +} + +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); + + PHP_HTTP_COOKIE_OBJECT_INIT(obj); + + array_init(return_value); + 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->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.clone_obj = php_http_cookie_object_clone; + + 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); + + 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 --git a/src/php_http_cookie.h b/src/php_http_cookie.h new file mode 100644 index 0000000..7cf00fe --- /dev/null +++ b/src/php_http_cookie.h @@ -0,0 +1,84 @@ +/* + +--------------------------------------------------------------------+ + | 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_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) +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); + +#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) +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 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 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; +} 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); + +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 --git a/src/php_http_curl.c b/src/php_http_curl.c new file mode 100644 index 0000000..a995094 --- /dev/null +++ b/src/php_http_curl.c @@ -0,0 +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" + +#if PHP_HTTP_HAVE_CURL + +#if defined(ZTS) && defined(PHP_HTTP_HAVE_SSL) +# ifdef PHP_WIN32 +# define PHP_HTTP_NEED_OPENSSL_TSL +# include +# else /* !PHP_WIN32 */ +# if defined(PHP_HTTP_HAVE_OPENSSL) +# define PHP_HTTP_NEED_OPENSSL_TSL +# include +# elif defined(PHP_HTTP_HAVE_GNUTLS) +# define PHP_HTTP_NEED_GNUTLS_TSL +# include +# endif /* PHP_HTTP_HAVE_OPENSSL || PHP_HTTP_HAVE_GNUTLS */ +# endif /* PHP_WIN32 */ +#endif /* ZTS && PHP_HTTP_HAVE_SSL */ + + +#ifdef PHP_HTTP_NEED_OPENSSL_TSL +static MUTEX_T *php_http_openssl_tsl = NULL; + +static void php_http_openssl_thread_lock(int mode, int n, const char * file, int line) +{ + if (mode & CRYPTO_LOCK) { + tsrm_mutex_lock(php_http_openssl_tsl[n]); + } else { + tsrm_mutex_unlock(php_http_openssl_tsl[n]); + } +} + +static ulong php_http_openssl_thread_id(void) +{ + return (ulong) tsrm_thread_id(); +} +#endif +#ifdef PHP_HTTP_NEED_GNUTLS_TSL +static int php_http_gnutls_mutex_create(void **m) +{ + if (*((MUTEX_T *) m) = tsrm_mutex_alloc()) { + return SUCCESS; + } else { + return FAILURE; + } +} + +static int php_http_gnutls_mutex_destroy(void **m) +{ + tsrm_mutex_free(*((MUTEX_T *) m)); + return SUCCESS; +} + +static int php_http_gnutls_mutex_lock(void **m) +{ + return tsrm_mutex_lock(*((MUTEX_T *) m)); +} + +static int php_http_gnutls_mutex_unlock(void **m) +{ + return tsrm_mutex_unlock(*((MUTEX_T *) m)); +} + +static struct gcry_thread_cbs php_http_gnutls_tsl = { + GCRY_THREAD_OPTION_USER, + NULL, + php_http_gnutls_mutex_create, + php_http_gnutls_mutex_destroy, + php_http_gnutls_mutex_lock, + php_http_gnutls_mutex_unlock +}; +#endif + + +PHP_MINIT_FUNCTION(http_curl) +{ +#ifdef PHP_HTTP_NEED_OPENSSL_TSL + /* mod_ssl, libpq or ext/curl might already have set thread lock callbacks */ + if (!CRYPTO_get_id_callback()) { + int i, c = CRYPTO_num_locks(); + + php_http_openssl_tsl = malloc(c * sizeof(MUTEX_T)); + + for (i = 0; i < c; ++i) { + php_http_openssl_tsl[i] = tsrm_mutex_alloc(); + } + + CRYPTO_set_id_callback(php_http_openssl_thread_id); + CRYPTO_set_locking_callback(php_http_openssl_thread_lock); + } +#endif +#ifdef PHP_HTTP_NEED_GNUTLS_TSL + gcry_control(GCRYCTL_SET_THREAD_CBS, &php_http_gnutls_tsl); +#endif + + if (CURLE_OK != curl_global_init(CURL_GLOBAL_ALL)) { + return FAILURE; + } + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(http_curl) +{ + curl_global_cleanup(); +#ifdef PHP_HTTP_NEED_OPENSSL_TSL + if (php_http_openssl_tsl) { + int i, c = CRYPTO_num_locks(); + + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < c; ++i) { + tsrm_mutex_free(php_http_openssl_tsl[i]); + } + + free(php_http_openssl_tsl); + php_http_openssl_tsl = NULL; + } +#endif + 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 --git a/src/php_http_curl.h b/src/php_http_curl.h new file mode 100644 index 0000000..ab8b63c --- /dev/null +++ b/src/php_http_curl.h @@ -0,0 +1,36 @@ +/* + +--------------------------------------------------------------------+ + | 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_CURL_H +#define PHP_HTTP_CURL_H + +#if PHP_HTTP_HAVE_CURL + +#include +#define PHP_HTTP_CURL_VERSION(x, y, z) (LIBCURL_VERSION_NUM >= (((x)<<16) + ((y)<<8) + (z))) + +PHP_MINIT_FUNCTION(http_curl); +PHP_MSHUTDOWN_FUNCTION(http_curl); + +#endif /* PHP_HTTP_HAVE_CURL */ + +#endif /* PHP_HTTP_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 --git a/src/php_http_encoding.c b/src/php_http_encoding.c new file mode 100644 index 0000000..286f2b5 --- /dev/null +++ b/src/php_http_encoding.c @@ -0,0 +1,1227 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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"); + 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); + 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)); + } 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); + } + } + 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); + 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) +{ + 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)); + return FAILURE; +} + +ZEND_RESULT_CODE php_http_encoding_inflate(const char *data, size_t data_len, char **decoded, size_t *decoded_len TSRMLS_DC) +{ + 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)); + 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) +{ + 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)); + 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)); + 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)); + 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)); + 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"); + 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)); + 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)); + 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"); + 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_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)); + 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)); + 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)); + 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) +{ + return php_http_encoding_stream_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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; + } + + 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; + + return o->zv; +} + +zend_object_value php_http_encoding_stream_object_clone(zval *this_ptr TSRMLS_DC) +{ + zend_object_value new_ov; + php_http_encoding_stream_object_t *new_obj = NULL, *old_obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + return new_ov; +} + +void php_http_encoding_stream_object_free(void *object TSRMLS_DC) +{ + php_http_encoding_stream_object_t *o = (php_http_encoding_stream_object_t *) object; + + if (o->stream) { + php_http_encoding_stream_free(&o->stream); + } + zend_object_std_dtor((zend_object *) o TSRMLS_CC); + efree(o); +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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)) { + ops = &php_http_encoding_deflate_ops; + } else if (instanceof_function(obj->zo.ce, php_http_inflate_stream_class_entry TSRMLS_CC)) { + ops = &php_http_encoding_inflate_ops; + } else if (instanceof_function(obj->zo.ce, php_http_dechunk_stream_class_entry TSRMLS_CC)) { + ops = &php_http_encoding_dechunk_ops; + } else { + php_http_throw(runtime, "Unknown http\\Encoding\\Stream class '%s'", obj->zo.ce->name); + return; + } + + php_http_expect(obj->stream = php_http_encoding_stream_init(obj->stream, ops, flags TSRMLS_CC), 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; + 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 (obj->stream) { + 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); + } + } + } +} + +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); + + if (obj->stream) { + char *encoded_str; + 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); + } 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); + + 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); + + if (obj->stream) { + char *encoded_str; + 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); + } 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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags)) { + char *enc_str; + 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); + } + } + 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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { + char *enc_str; + 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); + } + } + 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; + zval *zlen = NULL; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z!", &str, &len, &zlen)) { + const char *end_ptr; + char *enc_str; + size_t enc_len; + + if ((end_ptr = php_http_encoding_dechunk(str, len, &enc_str, &enc_len TSRMLS_CC))) { + if (zlen) { + zval_dtor(zlen); + ZVAL_LONG(zlen, str + len - end_ptr); + } + RETURN_STRINGL(enc_str, enc_len, 0); + } + } + 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->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.clone_obj = php_http_encoding_stream_object_clone; + + 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); + + 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); + + 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); + + 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); + + 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 --git a/src/php_http_encoding.h b/src/php_http_encoding.h new file mode 100644 index 0000000..cdf7a1a --- /dev/null +++ b/src/php_http_encoding.h @@ -0,0 +1,197 @@ +/* + +--------------------------------------------------------------------+ + | 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_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); + +typedef struct php_http_encoding_stream_object { + zend_object zo; + zend_object_value zv; + php_http_encoding_stream_t *stream; +} 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); + +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 --git a/src/php_http_env.c b/src/php_http_env.c new file mode 100644 index 0000000..c60d4c0 --- /dev/null +++ b/src/php_http_env.c @@ -0,0 +1,819 @@ +/* + +--------------------------------------------------------------------+ + | 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); + PHP_HTTP_G->env.server_var = NULL; + } + + return SUCCESS; +} + +void php_http_env_get_request_headers(HashTable *headers TSRMLS_DC) +{ + php_http_array_hashkey_t key = php_http_array_hashkey_init(0); + zval **hsv, **header; + HashPosition pos; + + 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); + + 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); + + 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); + + 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); + + 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); + + efree(key.str); + } + } + } + } + + if (headers) { + zend_hash_copy(headers, PHP_HTTP_G->env.request.headers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + } +} + +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) +{ + HashTable *request_headers; + 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); + 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); + + val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); + if (len) { + *len = Z_STRLEN_P(zcopy); + } + zval_ptr_dtor(&zcopy); + } + + 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) +{ + HashTable *request_headers; + char *key = php_http_pretty_key(estrndup(name_str, name_len), name_len, 1, 1); + int got; + + if (request) { + request_headers = &request->hdrs; + } else { + php_http_env_get_request_headers(NULL TSRMLS_CC); + request_headers = PHP_HTTP_G->env.request.headers; + } + got = zend_symtable_exists(request_headers, key, name_len + 1); + efree(key); + + return got; +} + +zval *php_http_env_get_superglobal(const char *key, size_t key_len TSRMLS_DC) +{ + zval **hsv; + + zend_is_auto_global(key, key_len TSRMLS_CC); + + if ((SUCCESS != zend_hash_find(&EG(symbol_table), key, key_len + 1, (void *) &hsv)) || (Z_TYPE_PP(hsv) != IS_ARRAY)) { + return NULL; + } + + return *hsv; +} + +zval *php_http_env_get_server_var(const char *key, size_t key_len, zend_bool check TSRMLS_DC) +{ + zval *hsv, **var; + char *env; + + /* 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)) { + 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))) { + return NULL; + } + if ((SUCCESS != zend_symtable_find(Z_ARRVAL_P(hsv), key, key_len + 1, (void *) &var))) { + return NULL; + } + if (check && !((Z_TYPE_PP(var) == IS_STRING) && Z_STRVAL_PP(var) && Z_STRLEN_PP(var))) { + return NULL; + } + return *var; +} + +php_http_message_body_t *php_http_env_get_request_body(TSRMLS_D) +{ + 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); + } + + return PHP_HTTP_G->env.request.body; +} + +const char *php_http_env_get_request_method(php_http_message_t *request TSRMLS_DC) +{ + 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) +{ + 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))) { + 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); + + 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) +{ + 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) +{ + 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); + php_http_buffer_fix(&headers); + + status = php_http_header_parse(headers.data, headers.used, headers_ht, NULL, NULL TSRMLS_CC); + 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 *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); + + if (SUCCESS == zend_symtable_find(&headers, key, name_len + 1, (void *) &zvalue)) { + zval *zcopy = php_http_ztyp(IS_STRING, *zvalue); + + val = estrndup(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy)); + zval_ptr_dtor(&zcopy); + } + + efree(key); + } + zend_hash_destroy(&headers); + + return val; +} + +long php_http_env_get_response_code(TSRMLS_D) +{ + 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) +{ + return sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) http_code TSRMLS_CC); +} + +ZEND_RESULT_CODE php_http_env_set_response_status_line(long code, php_http_version_t *v TSRMLS_DC) +{ + 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); + efree(h.line); + + return ret; +} + +ZEND_RESULT_CODE php_http_env_set_response_protocol_version(php_http_version_t *v TSRMLS_DC) +{ + return php_http_env_set_response_status_line(php_http_env_get_response_code(TSRMLS_C), v TSRMLS_CC); +} + +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) +{ + 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); + 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 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); + } + 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 ret; + va_list args; + + va_start(args, fmt); + ret = php_http_env_set_response_header_va(http_code, replace, fmt, args TSRMLS_CC); + 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) +{ + if (!value) { + sapi_header_line h = {(char *) name_str, name_len, http_code}; + + return sapi_header_op(SAPI_HEADER_DELETE, (void *) &h TSRMLS_CC); + } + + if(Z_TYPE_P(value) == IS_ARRAY || Z_TYPE_P(value) == IS_OBJECT) { + HashPosition pos; + int first = replace; + zval **data_ptr; + + 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)) { + return FAILURE; + } + first = 0; + } + + return SUCCESS; + } else { + zval *data = php_http_ztyp(IS_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); + } else { + sapi_header_line h; + ZEND_RESULT_CODE ret; + + if (name_len > INT_MAX) { + name_len = INT_MAX; + } + 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)); + + ret = sapi_header_op(replace ? SAPI_HEADER_REPLACE : SAPI_HEADER_ADD, (void *) &h TSRMLS_CC); + + zval_ptr_dtor(&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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); + + if (header_value) { + RETURN_STRINGL(header_value, header_length, 0); + } + } else { + array_init(return_value); + php_http_env_get_request_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); + } +} + +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; + 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); + + 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)) { + php_http_message_body_addref(body); + RETVAL_OBJVAL(ov, 0); + } +} + +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; + const char *status; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { + return; + } + + if ((status = php_http_env_get_response_status_for_code(code))) { + RETURN_STRING(status, 1); + } +} + +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); +#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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); + + if (header_value) { + RETURN_STRING(header_value, 0); + } + } else { + array_init(return_value); + php_http_env_get_response_headers(Z_ARRVAL_P(return_value) TSRMLS_CC); + } +} + +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)); +} + +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; + zval *header_value = NULL; + 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)) { + return; + } + RETURN_BOOL(SUCCESS == php_http_env_set_response_header_value(code, header_name_str, header_name_len, header_value, replace_header TSRMLS_CC)); +} + +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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &code)) { + return; + } + RETURN_BOOL(SUCCESS == php_http_env_set_response_code(code TSRMLS_CC)); +} + +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)) { + return; + } + if (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)) { + return; + } + if (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)) { + return; + } + if (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)) { + return; + } + if (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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sH|s!z", &value_str, &value_len, &supported, &sep_str, &sep_len, &rs_array)) { + return; + } + + + if (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))) { + 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); + + 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 --git a/src/php_http_env.h b/src/php_http_env.h new file mode 100644 index 0000000..3fc80ab --- /dev/null +++ b/src/php_http_env.h @@ -0,0 +1,88 @@ +/* + +--------------------------------------------------------------------+ + | 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); + +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 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_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 --git a/src/php_http_env_request.c b/src/php_http_env_request.c new file mode 100644 index 0000000..a884d2f --- /dev/null +++ b/src/php_http_env_request.c @@ -0,0 +1,286 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + zval *zfiles, **name, **zname, **error, **zerror, **type, **ztype, **size, **zsize, **tmp_name = zpp; + 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) + ) { + 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); + } else { + 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); + } + } + + return ZEND_HASH_APPLY_KEEP; +} + +static int grab_files(void *zpp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +{ + zval *zfiles, **name, **tmp_name, **error, **type, **size, **val = zpp; + + 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) + ) { + 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)) + ) { + zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp_name) TSRMLS_CC, 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); + } + if (key->nKeyLength > 0) { + zend_hash_quick_update(Z_ARRVAL_P(zfiles), key->arKey, key->nKeyLength, key->h, (void *) &cpy, sizeof(zval *), NULL); + } else { + zend_hash_index_update(Z_ARRVAL_P(zfiles), key->h, (void *) &cpy, sizeof(zval *), NULL); + } + } + } + + 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); \ + } \ + } 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; + + php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + obj->body = NULL; + + php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_REQUEST TSRMLS_CC), 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); + 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); + 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); + 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); + } + zend_update_property(php_http_env_request_class_entry, getThis(), ZEND_STRL("files"), zqs TSRMLS_CC); + 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); \ + \ + INIT_PZVAL(&mn); \ + array_init(&mn); \ + Z_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); \ + 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_args_clear(&fci, 1); \ + efree(args); \ + zval_dtor(&mn); \ + if (rv) { \ + 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); + 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); + 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); + 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); + 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); + + 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); + + 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 --git a/src/php_http_env_request.h b/src/php_http_env_request.h new file mode 100644 index 0000000..a97836b --- /dev/null +++ b/src/php_http_env_request.h @@ -0,0 +1,28 @@ +/* + +--------------------------------------------------------------------+ + | 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_REQUEST_H +#define PHP_HTTP_ENV_REQUEST_H + +PHP_HTTP_API zend_class_entry *php_http_env_request_class_entry; +PHP_MINIT_FUNCTION(http_env_request); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/php_http_env_response.c b/src/php_http_env_response.c new file mode 100644 index 0000000..20a31eb --- /dev/null +++ b/src/php_http_env_response.c @@ -0,0 +1,1513 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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); + break; + case IS_LONG: + zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC); + break; + case IS_STRING: + zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC); + break; + case IS_ARRAY: + case IS_OBJECT: + zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC); + break; + } + } else { + zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC); + } + } 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); + break; + case IS_LONG: + add_assoc_long_ex(options, name_str, name_len + 1, *(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); + break; + case IS_ARRAY: + case IS_OBJECT: + Z_ADDREF_P(value_ptr); + add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr); + break; + } + } + } else { + add_assoc_null_ex(options, name_str, name_len + 1); + } + } +} +static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC) +{ + zval *val, **valptr; + + if (Z_TYPE_P(options) == IS_OBJECT) { + val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC); + } else { + if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) { + val = *valptr; + } else { + val = NULL; + } + } + if (val) { + Z_ADDREF_P(val); + } + return val; +} +static php_http_message_body_t *get_body(zval *options TSRMLS_DC) +{ + zval *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); + + body = body_obj->body; + } + zval_ptr_dtor(&zbody); + } + + return body; +} +static php_http_message_t *get_request(zval *options TSRMLS_DC) +{ + zval *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); + + request = request_obj->message; + } + zval_ptr_dtor(&zrequest); + } + + return request; +} +static void set_cookie(zval *options, zval *zcookie_new TSRMLS_DC) +{ + 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); + } + + FOREACH_HASH_KEY(pos, &obj->list->cookies, 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); + } else { + add_index_zval(zcookies_set, key.num, zcookie_new); + } + } + + set_option(options, ZEND_STRL("cookies"), IS_ARRAY, zcookies_set, 0 TSRMLS_CC); + zval_ptr_dtor(&zcookies_set); +} + +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 ret = PHP_HTTP_CACHE_NO; + int free_etag = 0; + char *header = NULL, *etag; + php_http_message_body_t *body; + zval *zetag; + + + if (!(body = get_body(options TSRMLS_CC))) { + 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 && 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 (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); + } + + 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 ret = PHP_HTTP_CACHE_NO; + char *header; + time_t ums, lm = 0; + php_http_message_body_t *body; + zval *zlm; + + if (!(body = get_body(options TSRMLS_CC))) { + 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 && Z_LVAL_P(zlm) > 0) { + lm = Z_LVAL_P(zlm); + } else { + 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); + } + + if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) { + 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)) { + return 0; + } + + if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) { + return 0; + } + + return 1; +} + +static size_t output(void *context, char *buf, size_t len TSRMLS_DC) +{ + 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); + PTR_FREE(enc_str); + } else { + chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC); + } + + 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) +{ + 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); + + 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); + + if (r->done) { + return ret; + } + + if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) { + 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); + } + 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); + + 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_copy); + } + + if (ret != SUCCESS) { + return ret; + } + + if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) { + php_http_version_t v; + zval *zoption_copy = php_http_ztyp(IS_STRING, zoption); + + zval_ptr_dtor(&zoption); + if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) { + ret = r->ops->set_protocol_version(r, &v); + php_http_version_dtor(&v); + } + zval_ptr_dtor(&zoption_copy); + } + + if (ret != SUCCESS) { + return ret; + } + + if ((zoption = get_option(options, ZEND_STRL("cookies") TSRMLS_CC))) { + if (Z_TYPE_P(zoption) == IS_ARRAY) { + HashPosition pos; + 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); + 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); + } + } + } + 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); + + 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_copy); + } + + 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; + + 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 (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); + } + } 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); + 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); + + 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_copy); + } + + 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; + + 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_dtor(&buf); + zval_ptr_dtor(&zoption_copy); + 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); + zval zsupported; + HashTable *result = NULL; + + zval_ptr_dtor(&zoption); + switch (Z_LVAL_P(zoption_copy)) { + 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); + + if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) { + char *key_str = NULL; + uint key_len = 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))) { + ret = FAILURE; + } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) { + r->content.encoding = estrndup(key_str, key_len - 1); + } + } 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))) { + ret = FAILURE; + } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) { + r->content.encoding = estrndup(key_str, key_len - 1); + } + } 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)) { + 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)) { + 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); + + 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)); + } else { + ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy)); + } + zval_ptr_dtor(&zoption_copy); + } + if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) { + zval *zoption_copy = php_http_ztyp(IS_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); + if (date) { + ret = r->ops->set_header(r, "Last-Modified: %s", date); + efree(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; + 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 ((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 (r->range.status == PHP_HTTP_RANGE_OK) { + if (zend_hash_num_elements(&r->range.values) == 1) { + /* single range */ + 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) + ) { + /* 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); + 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; + + FOREACH_HASH_VAL(pos, &r->range.values, chunk) { + zval **begin, **end; + + if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 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), + 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); + } + } + + 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); + + /* check for ranges */ + if ((body = get_body(r->options TSRMLS_CC))) { + 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); + + 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)) { + 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) + ) { + 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) + ) { + 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"); + return FAILURE; + } + + if (SUCCESS != php_http_env_response_send_body(r)) { + php_error_docref(NULL TSRMLS_CC, 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"); + 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); +} +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); +} +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); +} +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); + 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); + 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); +} +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_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) { + sapi_flush(TSRMLS_C); + } +#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; + } + 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); + + /* 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); + } + zend_hash_destroy(&ctx->header); + zend_list_delete(ctx->stream->rsrc_id); + 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) +{ + HashPosition pos; + 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); + } else { + zval *tmp = php_http_ztyp(IS_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:"))) { + ctx->chunked = 0; + } + } + php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + php_http_buffer_appends(buf, PHP_HTTP_CRLF); + zval_ptr_dtor(&tmp); + } + } +} +static ZEND_RESULT_CODE php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC) +{ + 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); + + /* 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); + 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; + + 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'; + + 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); + } else { + MAKE_STD_ZVAL(zheader); + ZVAL_STRINGL(zheader, header_str, header_len, 0); + + 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; + } +} +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); + 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)) { + 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)) { + 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)) { + 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->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); \ + } \ + } 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); + + php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), 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; + + 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); + + 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); + } + 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); + + set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC); + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return); + + set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC); + 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); + + zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC); + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return); + + set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC); + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return); + + set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC); + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return); + + set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC); + 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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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)); + } +} + +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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return); + + set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC); + 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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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)); + } +} + +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; + double delay = 1; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); + 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; + 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); + + zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); + switch (Z_TYPE_P(zcookie_new)) { + case IS_OBJECT: + if (instanceof_function(Z_OBJCE_P(zcookie_new), php_http_cookie_class_entry TSRMLS_CC)) { + 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); + 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); + } + zend_restore_error_handling(&zeh TSRMLS_CC); + + set_cookie(getThis(), zcookie_new TSRMLS_CC); + 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)) { + /* 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 + + 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); + 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)) { + 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); + + 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 --git a/src/php_http_env_response.h b/src/php_http_env_response.h new file mode 100644 index 0000000..e6a112f --- /dev/null +++ b/src/php_http_env_response.h @@ -0,0 +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 | + +--------------------------------------------------------------------+ +*/ + +#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; + + 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 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 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 --git a/src/php_http_etag.c b/src/php_http_etag.c new file mode 100644 index 0000000..3604ad8 --- /dev/null +++ b/src/php_http_etag.c @@ -0,0 +1,131 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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 --git a/src/php_http_etag.h b/src/php_http_etag.h new file mode 100644 index 0000000..bf6cf49 --- /dev/null +++ b/src/php_http_etag.h @@ -0,0 +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_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 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 --git a/src/php_http_exception.c b/src/php_http_exception.c new file mode 100644 index 0000000..25a33e6 --- /dev/null +++ b/src/php_http_exception.c @@ -0,0 +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) +{ + if (ex) { + zval *m = zend_read_property(Z_OBJCE_P(ex), ex, "message", lenof("message"), 0 TSRMLS_CC); + 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); + + /* + * 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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 --git a/src/php_http_exception.h b/src/php_http_exception.h new file mode 100644 index 0000000..969a351 --- /dev/null +++ b/src/php_http_exception.h @@ -0,0 +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__) + +/* 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); \ + if (!(test)) { \ + zend_restore_error_handling(&__zeh TSRMLS_CC); \ + fail; \ + } \ + zend_restore_error_handling(&__zeh TSRMLS_CC); \ + } 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 --git a/src/php_http_filter.c b/src/php_http_filter.c new file mode 100644 index 0000000..b6d967b --- /dev/null +++ b/src/php_http_filter.c @@ -0,0 +1,453 @@ +/* + +--------------------------------------------------------------------+ + | 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); + 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 +#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) +#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); \ + if (!__buck) { \ + pefree(__data, this->is_persistent); \ + return PSFS_ERR_FATAL; \ + } \ + \ + php_stream_bucket_append(buckets_out, __buck TSRMLS_CC); \ + } + +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); + + 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); + } + + 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); + 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_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_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); + } + + /* 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; + + 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_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); + } + + /* 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_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) +{ + zval **tmp = ¶ms; + 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); + + } + 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 (!(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 (!(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 --git a/src/php_http_filter.h b/src/php_http_filter.h new file mode 100644 index 0000000..21fe4db --- /dev/null +++ b/src/php_http_filter.h @@ -0,0 +1,29 @@ +/* + +--------------------------------------------------------------------+ + | 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_FILTER_H +#define PHP_HTTP_FILTER_H + +PHP_HTTP_API php_stream_filter_factory php_http_filter_factory; +PHP_MINIT_FUNCTION(http_filter); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_header.c b/src/php_http_header.c new file mode 100644 index 0000000..41601df --- /dev/null +++ b/src/php_http_header.c @@ -0,0 +1,383 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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"); + return FAILURE; + } + + if (!php_http_header_parser_init(&ctx TSRMLS_CC)) { + php_http_buffer_dtor(&buf); + php_error_docref(NULL TSRMLS_CC, 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) +{ + 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); + + 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); + + cb(cb_arg, crlf ? "Set-Cookie: %s" PHP_HTTP_CRLF : "Set-Cookie: %s", Z_STRVAL_P(strval)); + zval_ptr_dtor(&strval); + } + } + } else { + zval *strval = php_http_header_value_to_string(*header TSRMLS_CC); + + cb(cb_arg, crlf ? "%s: %s" PHP_HTTP_CRLF : "%s: %s", key.str, Z_STRVAL_P(strval)); + zval_ptr_dtor(&strval); + } + } + } +} + +void php_http_header_to_string(php_http_buffer_t *str, HashTable *headers TSRMLS_DC) +{ + php_http_header_to_callback(headers, 1, (php_http_pass_format_callback_t) php_http_buffer_appendf, str TSRMLS_CC); +} + +zval *php_http_header_value_to_string(zval *header TSRMLS_DC) +{ + 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); + } + + return ret; +} + +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; + + 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); + + 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); + 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_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; + + 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)) { + php_http_buffer_appends(&buf, ": "); + php_http_buffer_append(&buf, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); + } else { + php_http_buffer_appends(&buf, ":"); + } + zval_ptr_dtor(&zvalue); + + RETURN_PHP_HTTP_BUFFER_VAL(&buf); + } + 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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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 (zend_hash_num_elements(&ht)) { + zval **val, *cpy; + char *str; + uint len; + ulong idx; + + zend_hash_internal_pointer_reset(&ht); + switch (zend_hash_get_current_key_ex(&ht, &str, &len, &idx, 0, NULL)) { + case HASH_KEY_IS_STRING: + zend_update_property_stringl(php_http_header_class_entry, getThis(), ZEND_STRL("name"), str, len - 1 TSRMLS_CC); + break; + case HASH_KEY_IS_LONG: + zend_update_property_long(php_http_header_class_entry, getThis(), ZEND_STRL("name"), idx TSRMLS_CC); + 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); + } + } + 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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|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); +} + +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; + char *sep_str = NULL; + size_t sep_len = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|z", &supported, &rs_array)) { + return; + } + if (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")) { + sep_str = "/"; + sep_len = 1; + } else if (!strcasecmp(Z_STRVAL_P(zname), "Accept-Language")) { + sep_str = "-"; + sep_len = 1; + } + 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 ((rs = php_http_negotiate(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue), supported, sep_str, sep_len TSRMLS_CC))) { + 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_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; + + INIT_PZVAL(&zctor); + ZVAL_STRINGL(&zctor, "__construct", lenof("__construct"), 0); + + MAKE_STD_ZVAL(zparams_obj); + 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); + 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 (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; + zend_class_entry *ce = NULL; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)) { + 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; + + Z_ADDREF_PP(val); + zvalue = *val; + + MAKE_STD_ZVAL(zkey); + if (key.type == HASH_KEY_IS_LONG) { + ZVAL_LONG(zkey, key.num); + } else { + ZVAL_STRINGL(zkey, key.str, key.len - 1, 1); + } + + MAKE_STD_ZVAL(zho); + object_init_ex(zho, ce); + zend_call_method_with_2_params(&zho, ce, NULL, "__construct", NULL, zkey, zvalue); + + if (key.type == HASH_KEY_IS_LONG) { + zend_hash_index_update(Z_ARRVAL_P(return_value), key.num, (void *) &zho, sizeof(zval *), NULL); + } else { + zend_hash_update(Z_ARRVAL_P(return_value), key.str, key.len, (void *) &zho, sizeof(zval *), NULL); + } + + zval_ptr_dtor(&zvalue); + zval_ptr_dtor(&zkey); + } + } + } + } +} + +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); + + 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 --git a/src/php_http_header.h b/src/php_http_header.h new file mode 100644 index 0000000..a2baecb --- /dev/null +++ b/src/php_http_header.h @@ -0,0 +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_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 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 zval *php_http_header_value_to_string(zval *header TSRMLS_DC); + +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 --git a/src/php_http_header_parser.c b/src/php_http_header_parser.c new file mode 100644 index 0000000..46551e2 --- /dev/null +++ b/src/php_http_header_parser.c @@ -0,0 +1,489 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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) +{ + int escaped_len; + char *escaped_str; + + escaped_str = php_addcslashes(str, len, &escaped_len, 0, ZEND_STRL("\x0..\x1F\x7F..\xFF") TSRMLS_CC); + + 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); + } 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); + } 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); + } + + 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"); + 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)) { + /* new message starting with request/response line */ + if (callback_func) { + callback_func(callback_arg, &headers, &parser->info TSRMLS_CC); + } + 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); + 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); + 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; + 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); + + 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); + } + + 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); + } else { + add_assoc_stringl_ex(&array, parser->_key.str, parser->_key.len + 1, parser->_val.str, parser->_val.len, 0); + } + 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) +{ + return php_http_header_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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; + } + + if (parser) { + o->parser = parser; + } else { + o->parser = php_http_header_parser_init(NULL TSRMLS_CC); + } + 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; + + return o->zv; +} + +void php_http_header_parser_object_free(void *object TSRMLS_DC) +{ + php_http_header_parser_object_t *o = (php_http_header_parser_object_t *) object; + + 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_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); + + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); + + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } + 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_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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); + + if (Z_TYPE_P(zmsg) != IS_ARRAY) { + zval_dtor(zmsg); + array_init(zmsg); + } + parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); + 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); + 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.clone_obj = NULL; + + 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("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); + + 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 --git a/src/php_http_header_parser.h b/src/php_http_header_parser.h new file mode 100644 index 0000000..ed9ecaf --- /dev/null +++ b/src/php_http_header_parser.h @@ -0,0 +1,80 @@ +/* + +--------------------------------------------------------------------+ + | 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_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; +} 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); + +#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 --git a/src/php_http_info.c b/src/php_http_info.c new file mode 100644 index 0000000..4fb067f --- /dev/null +++ b/src/php_http_info.c @@ -0,0 +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) +{ + 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) +{ + 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); + + /* and nothing than SPACE or NUL after HTTP/X.x */ + if (!php_http_version_parse(&info->http.version, http TSRMLS_CC) + || (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; + 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); + } else { + PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, ~0 TSRMLS_CC); + } + 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 --git a/src/php_http_info.h b/src/php_http_info.h new file mode 100644 index 0000000..4f02908 --- /dev/null +++ b/src/php_http_info.h @@ -0,0 +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_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; + +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); + +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 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 --git a/src/php_http_message.c b/src/php_http_message.c new file mode 100644 index 0000000..c6b03ff --- /dev/null +++ b/src/php_http_message.c @@ -0,0 +1,2085 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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)->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) +{ + 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); + + return message; +} + +php_http_message_t *php_http_message_init_env(php_http_message_t *message, php_http_message_type_t type TSRMLS_DC) +{ + 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); + 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); + } + if ((sval = php_http_env_get_server_var(ZEND_STRL("REQUEST_METHOD"), 1 TSRMLS_CC))) { + 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); + } + + php_http_env_get_request_headers(&message->hdrs TSRMLS_CC); + 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)) { + 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)); + 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"); + 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_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); + + if ((free_msg = !msg)) { + msg = php_http_message_init(NULL, 0, NULL TSRMLS_CC); + } + + 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 *ret = NULL, **header; + 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; + } + } + + 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_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); + + if (php_http_params_parse(¶ms, &popts TSRMLS_CC)) { + zval **cur, **arg; + char *ct_str; + + 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 (php_http_match(ct_str, "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 + ) { + zval **val; + HashPosition pos; + php_http_array_hashkey_t key = php_http_array_hashkey_init(0); + + 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); + + if (Z_STRLEN_P(bnd)) { + *boundary = estrndup(Z_STRVAL_P(bnd), Z_STRLEN_P(bnd)); + } + zval_ptr_dtor(&bnd); + } + } + } + } + } + } + zend_hash_destroy(¶ms); + zval_ptr_dtor(&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; + size_t size; + + 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))) { + /* 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); + + if (msg->body->boundary) { + char *str; + size_t len; + + if (!(h = php_http_message_header(msg, ZEND_STRL("Content-Type"), 1))) { + 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); + } else { + zval_ptr_dtor(&h); + } + } + } 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)) { + /* body->size == 0, so get rid of old Content-Length */ + zend_hash_del(&msg->hdrs, "Content-Length", sizeof("Content-Length")); + } + zval_ptr_dtor(&h_cpy); + } +} + +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); +} + +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); + + 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 *dst = php_http_message_copy(one, NULL), *src = php_http_message_copy(two, NULL), *tmp_dst, *tmp_src, *ret = dst; + + 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); + php_http_message_set_info(temp, &info); + zend_hash_copy(&temp->hdrs, &from->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + 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); + 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 *)); + + 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 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); + +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); +} +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 void php_http_message_object_prophandler_get_type(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + 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_get_request_method(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + if (PHP_HTTP_MESSAGE_TYPE(REQUEST, obj->message) && obj->message->http.info.request.method) { + RETVAL_STRING(obj->message->http.info.request.method, 1); + } else { + RETVAL_NULL(); + } +} +static void php_http_message_object_prophandler_set_request_method(php_http_message_object_t *obj, zval *value TSRMLS_DC) { + 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); + } +} +static void php_http_message_object_prophandler_get_request_url(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + 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); + } else { + RETVAL_NULL(); + } +} +static void php_http_message_object_prophandler_set_request_url(php_http_message_object_t *obj, zval *value TSRMLS_DC) { + 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)); + } +} +static void php_http_message_object_prophandler_get_response_status(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + if (PHP_HTTP_MESSAGE_TYPE(RESPONSE, obj->message) && obj->message->http.info.response.status) { + RETVAL_STRING(obj->message->http.info.response.status, 1); + } else { + RETVAL_NULL(); + } +} +static void php_http_message_object_prophandler_set_response_status(php_http_message_object_t *obj, zval *value TSRMLS_DC) { + 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); + } +} +static void php_http_message_object_prophandler_get_response_code(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + 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) { + 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); + 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) { + 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); +} +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_get_headers(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + 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 *)); +} +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); + + 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); +} +static void php_http_message_object_prophandler_get_body(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + if (obj->body) { + RETVAL_OBJVAL(obj->body->zv, 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_get_parent_message(php_http_message_object_t *obj, zval *return_value TSRMLS_DC) { + if (obj->message->parent) { + RETVAL_OBJVAL(obj->parent->zv, 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); + + if (obj->message->parent) { + zend_objects_store_del_ref_by_handle(obj->parent->zv.handle TSRMLS_CC); + } + Z_OBJ_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); \ + } \ + } while(0) + + +void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_DC) +{ + int i = 0; + php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + /* count */ + php_http_message_count(i, 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); + + efree(objects); + } else { + RETURN_ZVAL(getThis(), 1, 0); + } +} + +void php_http_message_object_prepend(zval *this_ptr, zval *prepend, zend_bool top TSRMLS_DC) +{ + 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; + + 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); + + 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) +{ + zval *tmp = NULL; + php_stream *s; + zend_object_value ov; + 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); + 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)) { + 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); + break; + } + /* no break */ + + default: + tmp = php_http_ztyp(IS_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; + 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); + } + if (msg_obj->body) { + zend_objects_store_del_ref_by_handle(msg_obj->body->zv.handle TSRMLS_CC); + } + 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); + } 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->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); +} + +zend_object_value php_http_message_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return php_http_message_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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; + } + + if (msg) { + o->message = msg; + if (msg->parent) { + php_http_message_object_new_ex(ce, msg->parent, &o->parent TSRMLS_CC); + } + 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->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; + + return o->zv; +} + +zend_object_value php_http_message_object_clone(zval *this_ptr TSRMLS_DC) +{ + 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); + + 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); + + return new_ov; +} + +void php_http_message_object_free(void *object TSRMLS_DC) +{ + php_http_message_object_t *o = (php_http_message_object_t *) object; + + if (o->iterator) { + zval_ptr_dtor(&o->iterator); + o->iterator = NULL; + } + 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); + o->parent = NULL; + } + if (o->body) { + zend_objects_store_del_ref_by_handle(o->body->zv.handle TSRMLS_CC); + o->body = NULL; + } + zend_object_std_dtor((zend_object *) o TSRMLS_CC); + efree(o); +} + +static zval *php_http_message_object_read_prop(zval *object, zval *member, int type PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) +{ + 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); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + 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 (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); + } + } else { + return_value = zend_get_std_object_handlers()->read_property(object, member, type PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); + } + + zval_ptr_dtor(©); + + return return_value; +} + +static void php_http_message_object_write_prop(zval *object, zval *member, zval *value PHP_HTTP_ZEND_LITERAL_DC TSRMLS_DC) +{ + php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); + php_http_message_object_prophandler_t *handler; + zval *copy = php_http_ztyp(IS_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); + } else { + zend_get_std_object_handlers()->write_property(object, member, value PHP_HTTP_ZEND_LITERAL_CC TSRMLS_CC); + } + + zval_ptr_dtor(©); +} + +static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) +{ + 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; + char *ver_str, *url_str = NULL; + size_t ver_len, url_len = 0; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + INIT_PZVAL_ARRAY(&array, props); + +#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) \ + 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); \ + } \ + } while(0) + + ASSOC_PROP(long, "type", 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); + + 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)); + 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); + } else { + ASSOC_STRINGL("requestUrl", "", 0); + } + + 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); + break; + + case PHP_HTTP_NONE: + default: + ASSOC_PROP(long, "responseCode", 0); + ASSOC_STRINGL("responseStatus", "", 0); + ASSOC_STRINGL("requestMethod", "", 0); + ASSOC_STRINGL("requestUrl", "", 0); + 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); + + MAKE_STD_ZVAL(body); + if (obj->body) { + ZVAL_OBJVAL(body, obj->body->zv, 1); + } else { + ZVAL_NULL(body); + } + ASSOC_PROP(zval, "body", body); + + MAKE_STD_ZVAL(parent); + if (obj->message->parent) { + ZVAL_OBJVAL(parent, obj->parent->zv, 1); + } else { + ZVAL_NULL(parent); + } + ASSOC_PROP(zval, "parentMessage", parent); + + 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); + zend_error_handling zeh; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy), invalid_arg, return); + + zend_replace_error_handling(EH_THROW, php_http_exception_bad_message_class_entry, &zeh TSRMLS_CC); + 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); + + if (s && php_http_message_parser_init(&p TSRMLS_CC)) { + 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); + + if (!msg && !EG(exception)) { + php_http_throw(bad_message, "Could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage)); + } + zval_ptr_dtor(&zmessage); + } + + 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); + } + } + zend_restore_error_handling(&zeh TSRMLS_CC); + 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); + + 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); + } +} + +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); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + php_http_message_object_prophandler_set_body(obj, zbody TSRMLS_CC); + } + 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); + + 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; + 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); + zval *header; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if ((header = php_http_message_header(obj->message, header_str, header_len, 0))) { + if (!header_ce) { + RETURN_ZVAL(header, 1, 1); + } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) { + 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); + + argv[0] = &header_name; + 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_dtor(&cb); + + zval_ptr_dtor(&header_name); + zval_ptr_dtor(&header); + + return; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class '%s' is not as descendant of http\\Header", header_ce->name); + } + } + } + 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_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; + + 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); + 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); + } else { + Z_ADDREF_P(zvalue); + zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); + } + 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); + + 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; + + 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); + 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))) { + convert_to_array(header); + zend_hash_next_index_insert(Z_ARRVAL_P(header), &zvalue, sizeof(void *), NULL); + zval_ptr_dtor(&header); + } else { + zend_symtable_update(&obj->message->hdrs, name, name_len + 1, &zvalue, sizeof(void *), NULL); + } + 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); + + 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_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; + + 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); + + 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); + + 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, "")); + 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, "")); + PTR_FREE(tmp); + break; + default: + RETURN_NULL(); + break; + } + Z_TYPE_P(return_value) = IS_STRING; + return; + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + if (!php_http_info_parse(&inf, str TSRMLS_CC)) { + 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_INIT(obj); + + php_http_version_to_string(&obj->message->http.version, &str, &len, NULL, NULL TSRMLS_CC); + RETURN_STRINGL(str, len, 0); + } +} + +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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + php_http_expect(php_http_version_parse(&version, v_str TSRMLS_CC), 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_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { + php_error_docref(NULL TSRMLS_CC, 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_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_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_INIT(obj); + + if (obj->message->type != PHP_HTTP_RESPONSE) { + php_error_docref(NULL TSRMLS_CC, 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); + } 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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { + php_error_docref(NULL TSRMLS_CC, 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); + } 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; + 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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_INIT(obj); + + if (obj->message->type != PHP_HTTP_REQUEST) { + php_error_docref(NULL TSRMLS_CC, 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); + } 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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); + + 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); + + 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); +} + +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); + 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_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); + 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); + } +} + +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); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + fcd.fcz = getThis(); + Z_ADDREF_P(fcd.fcz); + TSRMLS_SET_CTX(fcd.ts); + + 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); + 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_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; + 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); + php_http_message_t *msg; + + if (obj->message) { + php_http_message_dtor(obj->message); + efree(obj->message); + } + if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) { + 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"); + } + } +} + +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_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + 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); +} + +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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + prepend_obj = zend_object_store_get_object(prepend TSRMLS_CC); + 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); + 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); +} + +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); + char *boundary = NULL; + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + RETVAL_BOOL(php_http_message_is_multipart(obj->message, zboundary ? &boundary : NULL)); + + if (zboundary && boundary) { + zval_dtor(zboundary); + ZVAL_STRING(zboundary, boundary, 0); + } + } +} + +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); + + 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); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessage_count, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessage, count) +{ + 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); + + PHP_HTTP_MESSAGE_OBJECT_INIT(obj); + + php_http_message_count(i, obj->message); + RETURN_LONG(i); + } +} + +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); + + if (obj->iterator) { + zval_ptr_dtor(&obj->iterator); + } + Z_ADDREF_P(zobj); + 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); + + RETURN_BOOL(obj->iterator != NULL); + } +} + +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); + + if (obj->iterator) { + php_http_message_object_t *itr = zend_object_store_get_object(obj->iterator TSRMLS_CC); + + 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); + } else { + zval_ptr_dtor(&obj->iterator); + obj->iterator = NULL; + } + } + } +} + +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); + + RETURN_LONG(obj->iterator ? obj->iterator->value.obj.handle:0); + } +} + +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); + + if (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->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.clone_obj = php_http_message_object_clone; + 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_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_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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + + 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 --git a/src/php_http_message.h b/src/php_http_message.h new file mode 100644 index 0000000..780ea68 --- /dev/null +++ b/src/php_http_message.h @@ -0,0 +1,103 @@ +/* + +--------------------------------------------------------------------+ + | 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" + +/* 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 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_copy_ex(php_http_message_t *from, php_http_message_t *to, zend_bool parents); +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 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)); \ +} + +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); + +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; +} 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); +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); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_message_body.c b/src/php_http_message_body.c new file mode 100644 index 0000000..c80c238 --- /dev/null +++ b/src/php_http_message_body.c @@ -0,0 +1,934 @@ +/* + +--------------------------------------------------------------------+ + | 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); + +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 *body; + + if (body_ptr && *body_ptr) { + body = *body_ptr; + ++body->refcount; + 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); + } else { + stream = php_stream_temp_create(TEMP_STREAM_DEFAULT, 0xffff); + php_stream_auto_cleanup(stream); + body->stream_id = php_stream_get_resource_id(stream); + } + TSRMLS_SET_CTX(body->ts); + + 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); + } + 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); + 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); + 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))) { + 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) +{ + 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); +} + +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); + } + + 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)) { + return FAILURE; + } + } + if (files) { + INIT_PZVAL_ARRAY(&tmp, files); + if (SUCCESS != add_recursive_files(body, NULL, &tmp)) { + 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); + + safe_name = php_addslashes(estrdup(name), strlen(name), NULL, 1 TSRMLS_CC); + + BOUNDARY_OPEN(body); + php_http_message_body_appendf( + body, + "Content-Disposition: form-data; name=\"%s\"" PHP_HTTP_CRLF + "" PHP_HTTP_CRLF, + safe_name + ); + php_http_message_body_append(body, value_str, value_len); + BOUNDARY_CLOSE(body); + + efree(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); + + 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); + + 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 + ); + php_stream_copy_to_stream_ex(in, php_http_message_body_stream(body), PHP_STREAM_COPY_ALL, NULL); + BOUNDARY_CLOSE(body); + + efree(safe_name); + efree(path_dup); + efree(bname); + + return SUCCESS; +} + +static inline char *format_key(uint type, char *str, ulong num, const char *prefix) { + char *new_key = NULL; + + if (prefix && *prefix) { + if (type == HASH_KEY_IS_STRING) { + spprintf(&new_key, 0, "%s[%s]", prefix, str); + } else { + spprintf(&new_key, 0, "%s[%lu]", prefix, num); + } + } else if (type == HASH_KEY_IS_STRING) { + new_key = estrdup(str); + } 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) +{ + 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); + + 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)) { + efree(str); + ht->nApplyCount--; + return FAILURE; + } + efree(str); + } + --ht->nApplyCount; + } + } 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); + } + + return SUCCESS; +} + +static ZEND_RESULT_CODE add_recursive_files(php_http_message_body_t *body, const char *name, zval *value) +{ + zval **zdata = NULL, **zfile, **zname, **ztype; + HashTable *ht; + TSRMLS_FETCH_FROM_CTX(body->ts); + + 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)) + ) { + zval **val; + HashPosition pos; + php_http_array_hashkey_t key = php_http_array_hashkey_init(0); + + 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 (SUCCESS != add_recursive_files(body, str, *val)) { + efree(str); + --ht->nApplyCount; + return FAILURE; + } + efree(str); + } + } + --ht->nApplyCount; + } + return SUCCESS; + } else { + php_stream *stream; + zval *zfc = php_http_ztyp(IS_STRING, *zfile); + + if (SUCCESS == zend_hash_find(ht, ZEND_STRS("data"), (void *) &zdata)) { + if (Z_TYPE_PP(zdata) == IS_RESOURCE) { + php_stream_from_zval_no_verify(stream, zdata); + } else { + zval *tmp = php_http_ztyp(IS_STRING, *zdata); + + stream = php_stream_memory_open(TEMP_STREAM_READONLY, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + zval_ptr_dtor(&tmp); + } + } else { + stream = php_stream_open_wrapper(Z_STRVAL_P(zfc), "r", REPORT_ERRORS|USE_PATH, NULL); + } + + if (!stream) { + zval_ptr_dtor(&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); + + efree(key); + zval_ptr_dtor(&znc); + zval_ptr_dtor(&ztc); + zval_ptr_dtor(&zfc); + if (!zdata || Z_TYPE_PP(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) +{ + 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->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); + 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.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); + } + + 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) +{ + return php_http_message_body_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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; + } + + 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; + + return o->zv; +} + +zend_object_value php_http_message_body_object_clone(zval *object TSRMLS_DC) +{ + 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_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); + + return new_ov; +} + +void php_http_message_body_object_free(void *object TSRMLS_DC) +{ + php_http_message_body_object_t *obj = object; + + php_http_message_body_free(&obj->body); + zend_object_std_dtor((zend_object *) obj TSRMLS_CC); + efree(obj); +} + +#define PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj) \ + do { \ + if (!obj->body) { \ + obj->body = php_http_message_body_init(NULL, NULL TSRMLS_CC); \ + } \ + } 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); + zval *zstream = NULL; + php_stream *stream; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream), invalid_arg, return); + + if (zstream) { + 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); + } +} + +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_INIT(obj); + + php_http_message_body_to_string(obj->body, &str, &len, 0, 0); + if (str) { + RETURN_STRINGL(str, len, 0); + } + } + 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; + + 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); + php_stream *s = php_stream_memory_open(0, us_str, us_len); + + obj->body = php_http_message_body_init(NULL, s TSRMLS_CC); + } +} + +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; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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_INIT(obj); + + 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; + + 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); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + fcd.fcz = getThis(); + Z_ADDREF_P(fcd.fcz); + TSRMLS_SET_CTX(fcd.ts); + + 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_INIT(obj); + + zend_list_addref(obj->body->stream_id); + RETVAL_RESOURCE(obj->body->stream_id); + } +} + +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_INIT(obj); + + if (obj->body->boundary) { + RETURN_STRING(obj->body->boundary, 1); + } + } +} + +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; + 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_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_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); + + obj = zend_object_store_get_object(getThis() TSRMLS_CC); + mobj = zend_object_store_get_object(zobj TSRMLS_CC); + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); + php_http_message_body_add_part(obj->body, mobj->message); + zend_restore_error_handling(&zeh TSRMLS_CC); + + 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); + char *etag; + + PHP_HTTP_MESSAGE_BODY_OBJECT_INIT(obj); + + if ((etag = php_http_message_body_etag(obj->body))) { + RETURN_STRING(etag, 0); + } 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; + + 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); + 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); + 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); + } + } + } +} + +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->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.clone_obj = php_http_message_body_object_clone; + zend_class_implements(php_http_message_body_class_entry TSRMLS_CC, 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 --git a/src/php_http_message_body.h b/src/php_http_message_body.h new file mode 100644 index 0000000..dc2c7a2 --- /dev/null +++ b/src/php_http_message_body.h @@ -0,0 +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_MESSAGE_BODY_H +#define PHP_HTTP_MESSAGE_BODY_H + +typedef struct php_http_message_body { + int stream_id; + php_stream_statbuf ssb; + 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 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_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 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()); +} + +typedef struct php_http_message_body_object { + zend_object zo; + zend_object_value zv; + php_http_message_body_t *body; +} 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); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_message_parser.c b/src/php_http_message_parser.c new file mode 100644 index 0000000..fae16f1 --- /dev/null +++ b/src/php_http_message_parser.c @@ -0,0 +1,705 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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); + + 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; + + /* 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")); + + /* 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); + } + + 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); + } + } + + /* 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)) + ) { + if (php_http_match(Z_STRVAL_P(h_con), "close", PHP_HTTP_MATCH_WORD)) { + 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; + } + } + 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 (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); + } + 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); + } + } + + 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 (h_cr) { + 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")] == '=' + ) + ) { + char *total_at = NULL, *end_at = NULL; + char *start_at = Z_STRVAL_PP(h_cr) + 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); + 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); + + 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 ((*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); + 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) +{ + return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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; + } + + if (parser) { + o->parser = parser; + } else { + o->parser = php_http_message_parser_init(NULL TSRMLS_CC); + } + o->buffer = php_http_buffer_new(); + + 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; +} + +void php_http_message_parser_object_free(void *object TSRMLS_DC) +{ + php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object; + + 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); +} + +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); + + 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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)); + + zval_dtor(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); + } +} + +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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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); + + 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)); + + zval_dtor(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); + } +} + +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); + 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); + + 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 --git a/src/php_http_message_parser.h b/src/php_http_message_parser.h new file mode 100644 index 0000000..0bac9da --- /dev/null +++ b/src/php_http_message_parser.h @@ -0,0 +1,85 @@ +/* + +--------------------------------------------------------------------+ + | 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_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_message_parser_t *parser; +} 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); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_misc.c b/src/php_http_misc.c new file mode 100644 index 0000000..8e2227d --- /dev/null +++ b/src/php_http_misc.c @@ -0,0 +1,283 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + return snprintf(buf, buf_len, "%15.15F", sapi_get_request_time(TSRMLS_C) * php_combined_lcg(TSRMLS_C)); +} + +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, ...) +{ + HashPosition pos; + unsigned argl = 0; + va_list argv; + + 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; + } + } + va_end(argv); + + return argl; +} + +void php_http_array_copy_strings(void *zpp) +{ + zval **zvpp = ((zval **) zpp); + + *zvpp = php_http_zsep(1, IS_STRING, *zvpp); +} + +int php_http_array_apply_append_func(void *pDest TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + int flags; + char *key = NULL; + HashTable *dst; + zval **data = NULL, *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_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); + } else { + zend_hash_index_find(dst, hash_key->h, (void *) &data); + } + + if (flags & ARRAY_JOIN_STRINGIFY) { + value = php_http_zsep(1, IS_STRING, value); + } else { + Z_ADDREF_P(value); + } + + if (data) { + if (Z_TYPE_PP(data) != IS_ARRAY) { + convert_to_array(*data); + } + 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); + } else { + zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); + } + + 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 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_STRINGIFY) { + value = php_http_zsep(1, IS_STRING, value); + } else { + Z_ADDREF_P(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); + 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 { + zend_hash_index_update(dst, hash_key->h, (void *) &value, sizeof(zval *), NULL); + } + } + + 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); + + 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); + 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 --git a/src/php_http_misc.h b/src/php_http_misc.h new file mode 100644 index 0000000..a4f579d --- /dev/null +++ b/src/php_http_misc.h @@ -0,0 +1,366 @@ +/* + +--------------------------------------------------------------------+ + | 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); +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 +#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) +{ + 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; + } + return z; +} + +static inline zval *php_http_ztyp(int type, zval *z) +{ + SEPARATE_ARG_IF_REF(z); + return (Z_TYPE_P(z) == type) ? z : php_http_zconv(type, z); +} + +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; + } +} + +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) +{ + zend_ini_entry *ini_entry; + + if (SUCCESS == zend_hash_find(EG(ini_directives), name_str, name_len + 1, (void *) &ini_entry)) { + if (orig && ini_entry->modified) { + *value_str = ini_entry->orig_value; + *value_len = (size_t) ini_entry->orig_value_length; + } else { + *value_str = ini_entry->value; + *value_len = (size_t) ini_entry->value_length; + } + return SUCCESS; + } + return FAILURE; +} + +/* return object(values) */ +#define RETVAL_OBJECT(o, addref) \ + RETVAL_OBJVAL((o)->value.obj, 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, ...); + +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} + +static inline void php_http_array_hashkey_stringify(php_http_array_hashkey_t *key) +{ + if (key->type != HASH_KEY_IS_STRING) { + key->len = spprintf(&key->str, 0, "%lu", key->num) + 1; + } +} + +static inline void php_http_array_hashkey_stringfree(php_http_array_hashkey_t *key) +{ + if (key->type != HASH_KEY_IS_STRING || key->dup) { + PTR_FREE(key->str); + } +} + +#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_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) + +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); + +/* 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; + 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 --git a/src/php_http_negotiate.c b/src/php_http_negotiate.c new file mode 100644 index 0000000..a74875b --- /dev/null +++ b/src/php_http_negotiate.c @@ -0,0 +1,173 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + zval result, *first, *second; + + first = *((zval **) (*((Bucket **) a))->pData); + second= *((zval **) (*((Bucket **) b))->pData); + + if (numeric_compare_function(&result, first, second TSRMLS_CC) != 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 +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; + + 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) +{ + 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); + 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); + + if (match > best_match) { + best_match = match; + q = val; + } + } + } + + 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); + } + + zval_ptr_dtor(&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 *result = NULL; + + if (value_str && value_len) { + unsigned i = 0; + zval arr, **val, **arg, **zq; + HashPosition pos; + HashTable params; + php_http_array_hashkey_t key = php_http_array_hashkey_init(1); + 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); + efree(opts.input.str); + + INIT_PZVAL(&arr); + array_init(&arr); + + FOREACH_HASH_KEYVAL(pos, ¶ms, 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); + } else { + q = 1.0 - ++i / 100.0; + } + + if (key.type == HASH_KEY_IS_STRING) { + add_assoc_double_ex(&arr, key.str, key.len, q); + } else { + add_index_double(&arr, key.num, q); + } + + PTR_FREE(key.str); + } + +#if 0 + zend_print_zval_r(&arr, 1 TSRMLS_CC); +#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_destroy(¶ms); + zval_dtor(&arr); + zend_hash_sort(result, zend_qsort, php_http_negotiate_sort, 0 TSRMLS_CC); + } + + 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 --git a/src/php_http_negotiate.h b/src/php_http_negotiate.h new file mode 100644 index 0000000..f7405b5 --- /dev/null +++ b/src/php_http_negotiate.h @@ -0,0 +1,140 @@ +/* + +--------------------------------------------------------------------+ + | 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); + +static inline HashTable *php_http_negotiate_language(HashTable *supported, php_http_message_t *request TSRMLS_DC) +{ + HashTable *result = NULL; + size_t length; + char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Language"), &length, request TSRMLS_CC); + + if (value) { + result = php_http_negotiate(value, length, supported, "-", 1 TSRMLS_CC); + } + PTR_FREE(value); + + return result; +} + +static inline HashTable *php_http_negotiate_encoding(HashTable *supported, php_http_message_t *request TSRMLS_DC) +{ + HashTable *result = NULL; + size_t length; + char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Encoding"), &length, request TSRMLS_CC); + + if (value) { + result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); + } + PTR_FREE(value); + + return result; +} + +static inline HashTable *php_http_negotiate_charset(HashTable *supported, php_http_message_t *request TSRMLS_DC) +{ + HashTable *result = NULL; + size_t length; + char *value = php_http_env_get_request_header(ZEND_STRL("Accept-Charset"), &length, request TSRMLS_CC); + + if (value) { + result = php_http_negotiate(value, length, supported, NULL, 0 TSRMLS_CC); + } + PTR_FREE(value); + + return result; +} + +static inline HashTable *php_http_negotiate_content_type(HashTable *supported, php_http_message_t *request TSRMLS_DC) +{ + HashTable *result = NULL; + size_t length; + char *value = php_http_env_get_request_header(ZEND_STRL("Accept"), &length, request TSRMLS_CC); + + if (value) { + result = php_http_negotiate(value, length, supported, "/", 1 TSRMLS_CC); + } + PTR_FREE(value); + + return result; +} + +#define PHP_HTTP_DO_NEGOTIATE_DEFAULT(supported) \ + { \ + zval **value; \ + \ + zend_hash_internal_pointer_reset((supported)); \ + if (SUCCESS == zend_hash_get_current_data((supported), (void *) &value)) { \ + 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; \ + \ + 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); \ + } \ + } + +#define PHP_HTTP_DO_NEGOTIATE_HANDLE_RESULT(result, supported, rs_array) \ + { \ + char *key; \ + uint key_len; \ + 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); \ + } 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_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))) { \ + 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 --git a/src/php_http_object.c b/src/php_http_object.c new file mode 100644 index 0000000..d57388d --- /dev/null +++ b/src/php_http_object.c @@ -0,0 +1,141 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + return php_http_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +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 *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); + + if (ptr) { + *ptr = o; + } + + 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->zv; +} + +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_object_value ov; + + 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); + return FAILURE; + } + + ov = create(ce, intern_ptr, obj_ptr TSRMLS_CC); + if (ovp) { + *ovp = ov; + } + return SUCCESS; +} + +static inline zend_function *get_object_method(zval *zobject, zval *zmeth TSRMLS_DC) +{ +#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; + 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); + + 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; + } +} + +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 rv; + zval *retval = NULL; + + Z_ADDREF_P(zobject); + cb->fci.object_ptr = zobject; + cb->fcc.object_ptr = zobject; + + cb->fci.retval_ptr_ptr = 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); + } + + rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC); + + zval_ptr_dtor(&zobject); + if (!retval_ptr && retval) { + zval_ptr_dtor(&retval); + } + + return rv; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/src/php_http_object.h b/src/php_http_object.h new file mode 100644 index 0000000..3f75932 --- /dev/null +++ b/src/php_http_object.h @@ -0,0 +1,49 @@ +/* + +--------------------------------------------------------------------+ + | 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 { + 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); + +typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC); + +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); + +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); +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 --git a/src/php_http_options.c b/src/php_http_options.c new file mode 100644 index 0000000..59b9c5f --- /dev/null +++ b/src/php_http_options.c @@ -0,0 +1,126 @@ +/* + +--------------------------------------------------------------------+ + | 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_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); + + return registry; +} + +ZEND_RESULT_CODE php_http_options_apply(php_http_options_t *registry, HashTable *options, void *userdata) +{ + HashPosition pos; + zval *val; + php_http_option_t *opt; + + FOREACH_HASH_VAL(pos, ®istry->options, opt) { + 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; + } + } + 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; + + 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.type = type; + opt.option = option; + + INIT_ZVAL(opt.defval); + switch ((opt.type = type)) { + case IS_BOOL: + ZVAL_BOOL(&opt.defval, 0); + break; + + case IS_LONG: + ZVAL_LONG(&opt.defval, 0); + break; + + case IS_STRING: + ZVAL_STRINGL(&opt.defval, NULL, 0, 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; +} + +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 NULL; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/php_http_options.h b/src/php_http_options.h new file mode 100644 index 0000000..2475383 --- /dev/null +++ b/src/php_http_options.h @@ -0,0 +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_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; + + ulong option; + zend_uchar type; + unsigned flags; + zval defval; + + php_http_option_set_callback_t setter; +}; + +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 --git a/src/php_http_params.c b/src/php_http_params.c new file mode 100644 index 0000000..5adeb91 --- /dev/null +++ b/src/php_http_params.c @@ -0,0 +1,1283 @@ +/* + +--------------------------------------------------------------------+ + | 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, + 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; + } current; + unsigned quotes:1; + unsigned escape:1; + unsigned rfc5987:1; +} php_http_params_state_t; + +static inline void sanitize_escaped(zval *zv TSRMLS_DC) +{ + 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); + } + + php_stripcslashes(Z_STRVAL_P(zv), &Z_STRLEN_P(zv)); +} + +static inline void quote_string(zval *zv, zend_bool force TSRMLS_DC) +{ + int len = Z_STRLEN_P(zv); + + 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); + + 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); + + str[0] = '"'; + memcpy(&str[1], Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + str[len-1] = '"'; + str[len] = '\0'; + + zval_dtor(&tmp); + ZVAL_STRINGL(zv, str, len, 0); + } +} + +static inline void prepare_escaped(zval *zv TSRMLS_DC) +{ + if (Z_TYPE_P(zv) == IS_STRING) { + quote_string(zv, 0 TSRMLS_CC); + } else { + zval_dtor(zv); + ZVAL_EMPTY_STRING(zv); + } +} + +static inline void sanitize_urlencoded(zval *zv TSRMLS_DC) +{ + 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) +{ + int len; + char *str = php_raw_url_encode(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &len); + + zval_dtor(zv); + ZVAL_STRINGL(zv, str, len, 0); +} + +static void sanitize_dimension(zval *zv TSRMLS_DC) +{ + zval *arr = NULL, *tmp = NULL, **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); + 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)); + return; + } + if (ptr - var == 0) { + ++var; + break; + } + /* no break */ + + case ']': + + MAKE_STD_ZVAL(tmp); + 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); + *ptr = chr; + } else { + zend_hash_next_index_insert(Z_ARRVAL_PP(cur), (void *) &tmp, sizeof(zval *), (void *) &cur); + } + + var = NULL; + break; + } + + ++ptr; + } + + if (zend_hash_num_elements(Z_ARRVAL_P(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); + } 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 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) +{ + HashTable *ht = HASH_OF(zvalue); + HashPosition pos; + php_http_array_hashkey_t key = php_http_array_hashkey_init(0); + zval **val; + php_http_buffer_t prefix; + + if (!ht->nApplyCount++) { + 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) { + /* 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); + } else { + php_http_buffer_appendf(&prefix, "%lu", key.num); + } + 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); + } else { + zval *cpy = php_http_ztyp(IS_STRING, *val); + + 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); + } + + php_http_buffer_cut(&prefix, keybuf->used, prefix.used - keybuf->used); + } + 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) +{ + char *eos; + + zval_dtor(zv); + php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { + sanitize_escaped(zv TSRMLS_CC); + } + + 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); + } + + if (flags & PHP_HTTP_PARAMS_DIMENSION) { + sanitize_dimension(zv TSRMLS_CC); + } +} + +static inline void sanitize_rfc5987(zval *zv, char **language, zend_bool *latin1 TSRMLS_DC) +{ + 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); + } +} + +static inline void sanitize_rfc5988(char *str, size_t len, zval *zv TSRMLS_DC) +{ + zval_dtor(zv); + php_trim(str, len, " ><", 3, zv, 3 TSRMLS_CC); +} + +static inline void prepare_rfc5988(zval *zv TSRMLS_DC) +{ + 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); +} + +static inline void sanitize_value(unsigned flags, char *str, size_t len, zval *zv, zend_bool rfc5987 TSRMLS_DC) +{ + char *language = NULL; + zend_bool latin1 = 0; + + zval_dtor(zv); + php_trim(str, len, NULL, 0, zv, 3 TSRMLS_CC); + + if (rfc5987) { + sanitize_rfc5987(zv, &language, &latin1 TSRMLS_CC); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { + sanitize_escaped(zv TSRMLS_CC); + } + + if ((flags & PHP_HTTP_PARAMS_URLENCODED) || (rfc5987 && language)) { + sanitize_urlencoded(zv TSRMLS_CC); + } + + if (rfc5987 && language) { + zval *tmp; + + if (latin1) { + utf8encode(zv); + } + + MAKE_STD_ZVAL(tmp); + ZVAL_COPY_VALUE(tmp, zv); + array_init(zv); + add_assoc_zval(zv, language, tmp); + PTR_FREE(language); + } +} + +static inline void prepare_key(unsigned flags, char *old_key, size_t old_len, char **new_key, size_t *new_len TSRMLS_DC) +{ + zval zv; + + INIT_PZVAL(&zv); + ZVAL_STRINGL(&zv, old_key, old_len, 1); + + if (flags & PHP_HTTP_PARAMS_URLENCODED) { + prepare_urlencoded(&zv TSRMLS_CC); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { + if (flags & PHP_HTTP_PARAMS_RFC5988) { + prepare_rfc5988(&zv TSRMLS_CC); + } else { + prepare_escaped(&zv TSRMLS_CC); + } + } + + *new_key = Z_STRVAL(zv); + *new_len = Z_STRLEN(zv); +} + +static inline void prepare_value(unsigned flags, zval *zv TSRMLS_DC) +{ + if (flags & PHP_HTTP_PARAMS_URLENCODED) { + prepare_urlencoded(zv TSRMLS_CC); + } + + if (flags & PHP_HTTP_PARAMS_ESCAPED) { + prepare_escaped(zv TSRMLS_CC); + } +} + +static void merge_param(HashTable *params, zval *zdata, zval ***current_param, zval ***current_args TSRMLS_DC) +{ + zval **ptr, **zdata_ptr; + php_http_array_hashkey_t hkey = php_http_array_hashkey_init(0); + +#if 0 + { + zval tmp; + INIT_PZVAL_ARRAY(&tmp, params); + fprintf(stderr, "params = "); + zend_print_zval_r(&tmp, 1 TSRMLS_CC); + 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); + + 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)) + ) { + 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); + *current_args = args; + + if (hkey.type == HASH_KEY_IS_STRING) { + zend_hash_update(params, hkey.str, hkey.len, (void *) &tmp, sizeof(zval *), (void *) &ptr); + } else { + zend_hash_index_update(params, hkey.num, (void *) &tmp, sizeof(zval *), (void *) &ptr); + } + } else { + /* merge */ + if (hkey.type == HASH_KEY_IS_STRING) { + zend_hash_find(params, hkey.str, hkey.len, (void *) &ptr); + } else { + zend_hash_index_find(params, hkey.num, (void *) &ptr); + } + + 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) + ) { + /* + * params = [arr => [value => [0 => 1]]] + * ^- ptr + * zdata = [arr => [0 => NULL]] + * ^- zdata_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) { + + /* 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)) { + 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); + break; + } + } else { + if (SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(ptr), hkey.num, (void *) &ptr)) { + 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); + break; + } else { + Z_ADDREF_PP(test_ptr); + zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &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); + } + 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); + } else { + zend_hash_next_index_insert(Z_ARRVAL_PP(ptr), (void *) test_ptr, sizeof(zval *), (void *) &ptr); + } + break; + } + } + + } + } + + /* bubble up */ + while (Z_TYPE_PP(ptr) == IS_ARRAY && SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(ptr), (void *) &ptr)); + *current_param = ptr; +} + +static void push_param(HashTable *params, php_http_params_state_t *state, const php_http_params_opts_t *opts TSRMLS_DC) +{ + 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); + } + state->rfc5987 = 0; + } else if (state->arg.str) { + if (0 < (state->arg.len = state->input.str - state->arg.str)) { + 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); + state->rfc5987 = rfc5987; + if (Z_TYPE(key) == IS_STRING && Z_STRLEN(key)) { + MAKE_STD_ZVAL(val); + ZVAL_TRUE(val); + + if (rfc5987) { + 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); + } else { + 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); + } + } 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); + } + } + zval_dtor(&key); + } + } else if (state->param.str) { + if (0 < (state->param.len = state->input.str - state->param.str)) { + zval *prm, *arg, *val, *key; + zend_bool rfc5987 = 0; + + MAKE_STD_ZVAL(key); + ZVAL_NULL(key); + if (opts->flags & PHP_HTTP_PARAMS_RFC5988) { + sanitize_rfc5988(state->param.str, state->param.len, key TSRMLS_CC); + } else { + sanitize_key(opts->flags, state->param.str, state->param.len, key, &rfc5987 TSRMLS_CC); + 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); + } else { + 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); + } else { + zend_hash_update(Z_ARRVAL_P(prm), "value", sizeof("value"), (void *) &val, sizeof(zval *), (void *) &state->current.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); + } + 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) +{ + 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) +{ + 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); + 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); + + skip_sep(sep_len, &state, opts->param, opts->arg, opts->val TSRMLS_CC); + + /* 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); + + skip_sep(sep_len, &state, NULL, opts->arg, opts->val TSRMLS_CC); + + /* 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); + + skip_sep(sep_len, &state, NULL, NULL, opts->val TSRMLS_CC); + + 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); + + 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) +{ + 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); + 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) +{ + HashTable *ht = HASH_OF(zvalue); + zval **zdata, *tmp; + php_http_array_hashkey_t key = php_http_array_hashkey_init(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)) + ) { + php_http_array_hashkey_stringify(&key); + 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); + + 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)); + 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) +{ + 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); + 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) +{ + zval *tmp = php_http_zsep(1, IS_STRING, zv); + + quote_string(tmp, 1 TSRMLS_CC); + php_http_buffer_append(buf, vss, vsl); + php_http_buffer_append(buf, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); + + zval_ptr_dtor(&tmp); +} + +static inline void shift_val(php_http_buffer_t *buf, zval *zvalue, const char *vss, size_t vsl, unsigned flags TSRMLS_DC) +{ + if (Z_TYPE_P(zvalue) != IS_BOOL) { + zval *tmp = php_http_zsep(1, IS_STRING, zvalue); + + 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)); + + zval_ptr_dtor(&tmp); + } else if (!Z_BVAL_P(zvalue)) { + php_http_buffer_append(buf, vss, vsl); + php_http_buffer_appends(buf, "0"); + } +} + +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) +{ + 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; + zend_bool rfc5987 = !strcmp(key_str, "*rfc5987*"); + + if (!rfc5987) { + shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); + } + FOREACH_KEYVAL(pos, zvalue, 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); + } else { + shift_arg(buf, key.str, key.len-1, *val, ass, asl, vss, vsl, flags TSRMLS_CC); + } + php_http_array_hashkey_stringfree(&key); + } + } else { + shift_key(buf, key_str, key_len, ass, asl, flags TSRMLS_CC); + + 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); + return; + } + break; + } + } + + shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); + } +} + +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) +{ + 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); + 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); + } else { + shift_arg(buf, key_str, key_len, zvalue, ass, asl, vss, vsl, flags TSRMLS_CC); + } + } else { + if (flags & PHP_HTTP_PARAMS_RFC5988) { + shift_rfc5988(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); + } else { + shift_key(buf, key_str, key_len, pss, psl, flags TSRMLS_CC); + } + shift_val(buf, zvalue, vss, vsl, flags TSRMLS_CC); + } +} + +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) +{ + zval **zparam; + HashPosition pos, pos1; + php_http_array_hashkey_t key = php_http_array_hashkey_init(0), key1 = php_http_array_hashkey_init(0); + zend_bool rfc5987 = 0; + + if (!buf) { + buf = php_http_buffer_init(NULL); + } + + FOREACH_HASH_KEYVAL(pos, params, key, zparam) { + zval **zvalue, **zargs; + + if (Z_TYPE_PP(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)) { + 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); + + if (Z_TYPE_PP(zparam) == IS_ARRAY && SUCCESS != zend_hash_find(Z_ARRVAL_PP(zparam), ZEND_STRS("arguments"), (void *) &zvalue)) { + if (zvalue == zparam) { + continue; + } + 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")) { + 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_buffer_shrink(buf); + php_http_buffer_fix(buf); + + return buf; +} + +php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC) +{ + zval **sep; + HashPosition pos; + php_http_params_token_t **ret, **tmp; + + if (!zv) { + return NULL; + } + + zv = php_http_ztyp(IS_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); + + if (Z_STRLEN_P(zt)) { + *tmp = emalloc(sizeof(**tmp)); + (*tmp)->str = estrndup(Z_STRVAL_P(zt), (*tmp)->len = Z_STRLEN_P(zt)); + ++tmp; + } + zval_ptr_dtor(&zt); + } + zval_ptr_dtor(&zv); + + *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; + zend_error_handling zeh; + + 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); + + zend_replace_error_handling(EH_THROW, php_http_exception_runtime_class_entry, &zeh TSRMLS_CC); + { + switch (ZEND_NUM_ARGS()) { + case 5: + zend_update_property_long(php_http_params_class_entry, getThis(), ZEND_STRL("flags"), flags TSRMLS_CC); + /* no break */ + case 4: + zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("val_sep"), val_sep TSRMLS_CC); + /* no break */ + case 3: + zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("arg_sep"), arg_sep TSRMLS_CC); + /* no break */ + case 2: + zend_update_property(php_http_params_class_entry, getThis(), ZEND_STRL("param_sep"), param_sep TSRMLS_CC); + /* 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); + break; + default: + zcopy = php_http_ztyp(IS_STRING, zparams); + if (Z_STRLEN_P(zcopy)) { + 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 + }; + + 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); + + 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); + 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); + } + } + zend_restore_error_handling(&zeh TSRMLS_CC); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpParams_toArray, 0, 0, 0) +ZEND_END_ARG_INFO(); +PHP_METHOD(HttpParams, toArray) +{ + zval *zparams; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + zparams = zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC); + 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; + 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)); + + 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); + } else { + zpsep = php_http_ztyp(IS_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); + } else { + zasep = php_http_ztyp(IS_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); + } else { + zvsep = php_http_ztyp(IS_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); + + zval_ptr_dtor(&zparams); + zval_ptr_dtor(&zpsep); + zval_ptr_dtor(&zasep); + zval_ptr_dtor(&zvsep); + zval_ptr_dtor(&zflags); + + RETVAL_PHP_HTTP_BUFFER_VAL(&buf); +} + +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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { + return; + } + + zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); + + if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { + RETVAL_BOOL(Z_TYPE_PP(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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { + return; + } + + zparams = php_http_ztyp(IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); + + if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(zparams), name_str, name_len + 1, (void *) &zparam)) { + 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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name_str, &name_len)) { + return; + } + + zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); + + 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); +} + +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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name_str, &name_len, &nvalue)) { + return; + } + + zparams = php_http_zsep(1, IS_ARRAY, zend_read_property(php_http_params_class_entry, getThis(), ZEND_STRL("params"), 0 TSRMLS_CC)); + + 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); + } else { + new_zparam = nvalue; + Z_ADDREF_P(new_zparam); + } + add_assoc_zval_ex(zparams, name_str, name_len + 1, new_zparam); + } else { + 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); + } else { + MAKE_STD_ZVAL(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); + } + } else { + zval *tmp = php_http_ztyp(IS_STRING, nvalue), *arr; + + 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); + } + + 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->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); + + 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 --git a/src/php_http_params.h b/src/php_http_params.h new file mode 100644 index 0000000..e1ebe27 --- /dev/null +++ b/src/php_http_params.h @@ -0,0 +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; + 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 php_http_params_token_t **php_http_params_separator_init(zval *zv TSRMLS_DC); +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 --git a/src/php_http_querystring.c b/src/php_http_querystring.c new file mode 100644 index 0000000..d72337f --- /dev/null +++ b/src/php_http_querystring.c @@ -0,0 +1,740 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + zval *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); + } + + php_http_querystring_update(qa, params, NULL TSRMLS_CC); + zend_update_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), qa TSRMLS_CC); + zval_ptr_dtor(&qa); +} + +static inline void php_http_querystring_str(zval *instance, zval *return_value TSRMLS_DC) +{ + zval *qa = zend_read_property(php_http_querystring_class_entry, instance, ZEND_STRL("queryArray"), 0 TSRMLS_CC); + + if (Z_TYPE_P(qa) == IS_ARRAY) { + php_http_querystring_update(qa, NULL, return_value TSRMLS_CC); + } 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) +{ + zval **arrval, *qarray = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); + + 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); + } else { + RETVAL_ZVAL(*arrval, 1, 0); + } + + if (del) { + 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); + 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) +{ + 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); + + 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); + 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); + } + 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); + return FAILURE; + } + if (key.type == HASH_KEY_IS_STRING) { + add_assoc_stringl_ex(dst, xkey, xlen+1, xlate_str, xlate_len, 0); + } else { + add_index_stringl(dst, key.num, xlate_str, xlate_len, 0); + } + } else if (Z_TYPE_PP(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); + } else { + add_index_zval(dst, key.num, subarray); + } + if (SUCCESS != php_http_querystring_xlate(subarray, *entry, ie, oe TSRMLS_CC)) { + if (key.type == HASH_KEY_IS_STRING) { + efree(xkey); + } + return FAILURE; + } + } + + if (key.type == HASH_KEY_IS_STRING) { + efree(xkey); + } + } + return SUCCESS; +} +#endif /* HAVE_ICONV */ + +ZEND_RESULT_CODE php_http_querystring_ctor(zval *instance, zval *params TSRMLS_DC) +{ + php_http_querystring_set(instance, params, 0 TSRMLS_CC); + return SUCCESS; +} + +static int apply_querystring(void *pData TSRMLS_DC) +{ + zval **val = pData; + + if (Z_TYPE_PP(val) == IS_ARRAY) { + zval **zvalue; + + 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); + } + } + + 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 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; + + MAKE_STD_ZVAL(arr); + array_init_size(arr, asi_len); + + do { + add_next_index_stringl(arr, asi_str++, 1, 1); + } while (*asi_str); + + opts.param = php_http_params_separator_init(arr TSRMLS_CC); + + zval_ptr_dtor(&arr); + } + + MAKE_STD_ZVAL(opts.defval); + ZVAL_NULL(opts.defval); + + if (php_http_params_parse(ht, &opts TSRMLS_CC)) { + zend_hash_apply(ht, apply_querystring TSRMLS_CC); + 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) +{ + /* 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; + + 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); + } else if (Z_TYPE_P(params) == IS_OBJECT || Z_TYPE_P(params) == IS_ARRAY) { + ptr = HASH_OF(params); + } else { + zv_ptr = php_http_ztyp(IS_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); + } + + FOREACH_HASH_KEYVAL(pos, ptr, key, params_entry) { + /* only public properties */ + if (key.type != HASH_KEY_IS_STRING || *key.str) { + if (Z_TYPE_PP(params_entry) == IS_NULL) { + /* + * delete + */ + if (key.type == HASH_KEY_IS_STRING) { + zend_hash_del(Z_ARRVAL_P(qarray), key.str, key.len); + } else { + zend_hash_index_del(Z_ARRVAL_P(qarray), key.num); + } + } 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)))) { + /* + * update + */ + zval equal, *entry = NULL; + + /* 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 (entry) { + if (key.type == HASH_KEY_IS_STRING) { + zend_hash_update(Z_ARRVAL_P(qarray), key.str, key.len, (void *) &entry, sizeof(zval *), NULL); + } else { + zend_hash_index_update(Z_ARRVAL_P(qarray), key.num, (void *) &entry, sizeof(zval *), NULL); + } + } + } else { + zval *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); + } else { + Z_ADDREF_PP(params_entry); + entry = *params_entry; + } + if (key.type == HASH_KEY_IS_STRING) { + add_assoc_zval_ex(qarray, key.str, key.len, entry); + } else { + add_index_zval(qarray, key.num, entry); + } + } + } + } + /* clean up */ + if (zv_ptr) { + zval_ptr_dtor(&zv_ptr); + } + 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)) { + zval_dtor(outstring); + ZVAL_STRINGL(outstring, s, l, 0); + } else { + php_error_docref(NULL TSRMLS_CC, 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); + + 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_BEGIN_ARG_INFO_EX(ai_HttpQueryString_getGlobalInstance, 0, 0, 0) +ZEND_END_ARG_INFO(); +PHP_METHOD(HttpQueryString, getGlobalInstance) +{ + zval *instance; + + 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); + + 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); + + 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); + + 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); + } + } + + 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; + + 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); + + 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_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); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpQueryString_toArray, 0, 0, 0) +ZEND_END_ARG_INFO(); +PHP_METHOD(HttpQueryString, toArray) +{ + zval *zqa; + + if (SUCCESS != zend_parse_parameters_none()) { + return; + } + + zqa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); + 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; + 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 (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); + } else { + php_http_querystring_str(getThis(), return_value TSRMLS_CC); + } + } +} + +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)) { + return; + } + + php_http_querystring_set(getThis(), params, QS_MERGE TSRMLS_CC); + 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; + zend_error_handling zeh; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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_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; \ + 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); \ + } \ +} +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; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "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)); + + php_http_expect(SUCCESS == php_http_querystring_xlate(na, qa, ie, oe TSRMLS_CC), bad_conversion, + zval_ptr_dtor(&na); + zval_ptr_dtor(&qa); + return; + ); + + php_http_querystring_set(getThis(), na, 0 TSRMLS_CC); + 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); +} + +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)) { + return; + } + + if (Z_TYPE_P(serialized) == IS_STRING) { + php_http_querystring_set(getThis(), serialized, 0 TSRMLS_CC); + } else { + php_error_docref(NULL TSRMLS_CC, 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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { + return; + } + + qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); + + 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); + } + } +} + +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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &offset_str, &offset_len, &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); + 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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { + return; + } + + qa = zend_read_property(php_http_querystring_class_entry, getThis(), ZEND_STRL("queryArray"), 0 TSRMLS_CC); + + 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); + } + } + 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; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &offset_str, &offset_len)) { + 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); + 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->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_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_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); + + 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 --git a/src/php_http_querystring.h b/src/php_http_querystring.h new file mode 100644 index 0000000..3391e91 --- /dev/null +++ b/src/php_http_querystring.h @@ -0,0 +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); +#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); + +typedef php_http_object_t php_http_querystring_object_t; + +#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 --git a/src/php_http_response_codes.h b/src/php_http_response_codes.h new file mode 100644 index 0000000..82157f9 --- /dev/null +++ b/src/php_http_response_codes.h @@ -0,0 +1,83 @@ +/* + +--------------------------------------------------------------------+ + | 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-2015, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +#ifndef PHP_HTTP_RESPONSE_CODE +# define PHP_HTTP_RESPONSE_CODE(code, status) +#endif + +PHP_HTTP_RESPONSE_CODE(100, "Continue") +PHP_HTTP_RESPONSE_CODE(101, "Switching Protocols") +PHP_HTTP_RESPONSE_CODE(102, "Processing") +PHP_HTTP_RESPONSE_CODE(200, "OK") +PHP_HTTP_RESPONSE_CODE(201, "Created") +PHP_HTTP_RESPONSE_CODE(202, "Accepted") +PHP_HTTP_RESPONSE_CODE(203, "Non-Authoritative Information") +PHP_HTTP_RESPONSE_CODE(204, "No Content") +PHP_HTTP_RESPONSE_CODE(205, "Reset Content") +PHP_HTTP_RESPONSE_CODE(206, "Partial Content") +PHP_HTTP_RESPONSE_CODE(207, "Multi-Status") +PHP_HTTP_RESPONSE_CODE(208, "Already Reported") +PHP_HTTP_RESPONSE_CODE(226, "IM Used") +PHP_HTTP_RESPONSE_CODE(300, "Multiple Choices") +PHP_HTTP_RESPONSE_CODE(301, "Moved Permanently") +PHP_HTTP_RESPONSE_CODE(302, "Found") +PHP_HTTP_RESPONSE_CODE(303, "See Other") +PHP_HTTP_RESPONSE_CODE(304, "Not Modified") +PHP_HTTP_RESPONSE_CODE(305, "Use Proxy") +PHP_HTTP_RESPONSE_CODE(307, "Temporary Redirect") +PHP_HTTP_RESPONSE_CODE(308, "Permanent Redirect") +PHP_HTTP_RESPONSE_CODE(400, "Bad Request") +PHP_HTTP_RESPONSE_CODE(401, "Unauthorized") +PHP_HTTP_RESPONSE_CODE(402, "Payment Required") +PHP_HTTP_RESPONSE_CODE(403, "Forbidden") +PHP_HTTP_RESPONSE_CODE(404, "Not Found") +PHP_HTTP_RESPONSE_CODE(405, "Method Not Allowed") +PHP_HTTP_RESPONSE_CODE(406, "Not Acceptable") +PHP_HTTP_RESPONSE_CODE(407, "Proxy Authentication Required") +PHP_HTTP_RESPONSE_CODE(408, "Request Timeout") +PHP_HTTP_RESPONSE_CODE(409, "Conflict") +PHP_HTTP_RESPONSE_CODE(410, "Gone") +PHP_HTTP_RESPONSE_CODE(411, "Length Required") +PHP_HTTP_RESPONSE_CODE(412, "Precondition Failed") +PHP_HTTP_RESPONSE_CODE(413, "Request Entity Too Large") +PHP_HTTP_RESPONSE_CODE(414, "Request URI Too Long") +PHP_HTTP_RESPONSE_CODE(415, "Unsupported Media Type") +PHP_HTTP_RESPONSE_CODE(416, "Requested Range Not Satisfiable") +PHP_HTTP_RESPONSE_CODE(417, "Expectation Failed") +PHP_HTTP_RESPONSE_CODE(422, "Unprocessible Entity") +PHP_HTTP_RESPONSE_CODE(423, "Locked") +PHP_HTTP_RESPONSE_CODE(424, "Failed Dependency") +PHP_HTTP_RESPONSE_CODE(426, "Upgrade Required") +PHP_HTTP_RESPONSE_CODE(428, "Precondition Required") +PHP_HTTP_RESPONSE_CODE(429, "Too Many Requests") +PHP_HTTP_RESPONSE_CODE(431, "Request Header Fields Too Large") +PHP_HTTP_RESPONSE_CODE(500, "Internal Server Error") +PHP_HTTP_RESPONSE_CODE(501, "Not Implemented") +PHP_HTTP_RESPONSE_CODE(502, "Bad Gateway") +PHP_HTTP_RESPONSE_CODE(503, "Service Unavailable") +PHP_HTTP_RESPONSE_CODE(504, "Gateway Timeout") +PHP_HTTP_RESPONSE_CODE(505, "HTTP Version Not Supported") +PHP_HTTP_RESPONSE_CODE(506, "Variant Also Negotiates") +PHP_HTTP_RESPONSE_CODE(507, "Insufficient Storage") +PHP_HTTP_RESPONSE_CODE(508, "Loop Detected") +PHP_HTTP_RESPONSE_CODE(510, "Not Extended") +PHP_HTTP_RESPONSE_CODE(511, "Network Authentication Required") + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/src/php_http_url.c b/src/php_http_url.c new file mode 100644 index 0000000..81b2d95 --- /dev/null +++ b/src/php_http_url.c @@ -0,0 +1,1749 @@ +/* + +--------------------------------------------------------------------+ + | 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) +{ + 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); + 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)) { + 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); + 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 *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); + + old_url = tmp_url = php_http_url_mod(env_url, old_url, flags ^ PHP_HTTP_URL_FROM_ENV TSRMLS_CC); + 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_NULL(&qstr); + php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC); + + 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) +{ + zval *zcpy; + 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); + } + + return purl; +} + +php_http_url_t *php_http_url_from_struct(HashTable *ht) +{ + 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); + 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); + } + if (SUCCESS == zend_hash_find(ht, "user", sizeof("user"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + if (SUCCESS == zend_hash_find(ht, "pass", sizeof("pass"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + if (SUCCESS == zend_hash_find(ht, "host", sizeof("host"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + 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 (SUCCESS == zend_hash_find(ht, "path", sizeof("path"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + if (SUCCESS == zend_hash_find(ht, "query", sizeof("query"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + if (SUCCESS == zend_hash_find(ht, "fragment", sizeof("fragment"), (void *) &e)) { + zval *cpy = php_http_ztyp(IS_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); + } + + return url(buf); +} + +HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct TSRMLS_DC) +{ + zval arr; + + 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)); + break; + } + } else { + INIT_PZVAL(&arr); + array_init(&arr); + } + + if (url) { + if (url->scheme) { + add_assoc_string(&arr, "scheme", url->scheme, 1); + } + if (url->user) { + add_assoc_string(&arr, "user", url->user, 1); + } + if (url->pass) { + add_assoc_string(&arr, "pass", url->pass, 1); + } + if (url->host) { + add_assoc_string(&arr, "host", url->host, 1); + } + if (url->port) { + add_assoc_long(&arr, "port", (long) url->port); + } + if (url->path) { + add_assoc_string(&arr, "path", url->path, 1); + } + if (url->query) { + add_assoc_string(&arr, "query", url->query, 1); + } + if (url->fragment) { + add_assoc_string(&arr, "fragment", url->fragment, 1); + } + } + + return Z_ARRVAL(arr); +} + +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) +{ + 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); + + 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)) { + 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) +{ + 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)) { + 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, + "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, + "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, + "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, + "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) +{ + 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); + 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); + 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)); + 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)); + 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)) { + 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)) { + return FAILURE; + } +#endif + } else { + php_error_docref(NULL TSRMLS_CC, 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); + 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"); + 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"); + return FAILURE; + } +#endif + } else { + php_error_docref(NULL TSRMLS_CC, 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"); + 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) +{ + 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); + 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, + "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, + "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, + "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, + "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, + "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, + "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, + "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, + "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, + "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) +{ + 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); + 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); + efree(state); + return NULL; + } + + if (!parse_fragment(state)) { + php_error_docref(NULL TSRMLS_CC, 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) +{ + 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, + "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_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); + + zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); + { + 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); + if (!new_purl) { + zend_restore_error_handling(&zeh TSRMLS_CC); + return; + } + } + if (old_url) { + old_purl = php_http_url_from_zval(old_url, flags TSRMLS_CC); + if (!old_purl) { + if (new_purl) { + php_http_url_free(&new_purl); + } + zend_restore_error_handling(&zeh TSRMLS_CC); + return; + } + } + + res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); + php_http_url_to_struct(res_purl, getThis() TSRMLS_CC); + + 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_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_error_handling zeh; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return); + + zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC); + { + 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); + if (!new_purl) { + zend_restore_error_handling(&zeh TSRMLS_CC); + 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); + + res_purl = php_http_url_mod(old_purl, new_purl, flags TSRMLS_CC); + php_http_url_to_struct(res_purl, return_value TSRMLS_CC); + + 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_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_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_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); + +#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); +#endif + zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC); +#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); +#endif + zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT TSRMLS_CC); + + 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 --git a/src/php_http_url.h b/src/php_http_url.h new file mode 100644 index 0000000..636efb5 --- /dev/null +++ b/src/php_http_url.h @@ -0,0 +1,99 @@ +/* + +--------------------------------------------------------------------+ + | 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_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 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); + +static inline void php_http_url_argsep(const char **str, size_t *len TSRMLS_DC) +{ + php_http_ini_entry(ZEND_STRL("arg_separator.output"), str, len, 0 TSRMLS_CC); +} + +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 --git a/src/php_http_utf8.h b/src/php_http_utf8.h new file mode 100644 index 0000000..f900803 --- /dev/null +++ b/src/php_http_utf8.h @@ -0,0 +1,670 @@ +/* + +--------------------------------------------------------------------+ + | 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_UTF8_H +#define PHP_HTTP_UTF8_H + +typedef struct utf8_range { + unsigned int start; + unsigned int end; + unsigned char step; +} utf8_range_t; + +static const unsigned char utf8_mblen[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 +}; + +static const unsigned char utf8_mask[] = { + 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 +}; + +static const utf8_range_t utf8_ranges[] = { +/* BEGIN::UTF8TABLE */ + { 0x0041, 0x005A, 1}, + { 0x0061, 0x007A, 1}, + { 0x00AA, 0, 0}, + { 0x00B5, 0, 0}, + { 0x00BA, 0, 0}, + { 0x00C0, 0x00D6, 1}, + { 0x00D8, 0x00F6, 1}, + { 0x00F8, 0x00FF, 1}, + { 0x0100, 0x017F, 1}, + { 0x0180, 0x024F, 1}, + { 0x0250, 0x02AF, 1}, + { 0x02B0, 0x02C1, 1}, + { 0x02C6, 0x02D1, 1}, + { 0x02E0, 0x02E4, 1}, + { 0x02EE, 0, 0}, + { 0x0345, 0, 0}, + { 0x0370, 0x0373, 1}, + { 0x0376, 0x0377, 1}, + { 0x037A, 0x037D, 1}, + { 0x0386, 0, 0}, + { 0x0388, 0x038A, 1}, + { 0x038C, 0, 0}, + { 0x038E, 0x03A1, 1}, + { 0x03A3, 0x03CE, 1}, + { 0x03D0, 0x03F5, 1}, + { 0x03F7, 0x03FF, 1}, + { 0x0400, 0x0481, 1}, + { 0x048A, 0x04FF, 1}, + { 0x0500, 0x0523, 1}, + { 0x0531, 0x0556, 1}, + { 0x0559, 0, 0}, + { 0x0561, 0x0587, 1}, + { 0x05D0, 0x05EA, 1}, + { 0x05F0, 0x05F2, 1}, + { 0x0621, 0x064A, 1}, + { 0x066E, 0x066F, 1}, + { 0x0671, 0x06D3, 1}, + { 0x06D5, 0, 0}, + { 0x06E5, 0x06E6, 1}, + { 0x06EE, 0x06EF, 1}, + { 0x06FA, 0x06FC, 1}, + { 0x06FF, 0, 0}, + { 0x0710, 0, 0}, + { 0x0712, 0x072F, 1}, + { 0x074D, 0x074F, 1}, + { 0x0750, 0x077F, 1}, + { 0x0780, 0x07A5, 1}, + { 0x07B1, 0, 0}, + { 0x07C0, 0x07EA, 1}, + { 0x07F4, 0x07F5, 1}, + { 0x07FA, 0, 0}, + { 0x0901, 0x0939, 1}, + { 0x093C, 0x094D, 1}, + { 0x0950, 0x0954, 1}, + { 0x0958, 0x0961, 1}, + { 0x0962, 0, 0}, + { 0x0963, 0, 0}, + { 0x0972, 0, 0}, + { 0x097B, 0x097F, 1}, + { 0x0981, 0x0983, 1}, + { 0x0985, 0x098C, 1}, + { 0x098F, 0, 0}, + { 0x0990, 0, 0}, + { 0x0993, 0x09A8, 1}, + { 0x09AA, 0x09B0, 1}, + { 0x09B2, 0, 0}, + { 0x09B6, 0x09B9, 1}, + { 0x09BC, 0x09C4, 1}, + { 0x09C7, 0, 0}, + { 0x09C8, 0, 0}, + { 0x09CB, 0x09CE, 1}, + { 0x09D7, 0, 0}, + { 0x09DC, 0, 0}, + { 0x09DD, 0, 0}, + { 0x09DF, 0x09E3, 1}, + { 0x09F0, 0x09FA, 1}, + { 0x0A01, 0x0A03, 1}, + { 0x0A05, 0x0A0A, 1}, + { 0x0A0F, 0, 0}, + { 0x0A10, 0, 0}, + { 0x0A13, 0x0A28, 1}, + { 0x0A2A, 0x0A30, 1}, + { 0x0A32, 0, 0}, + { 0x0A33, 0, 0}, + { 0x0A35, 0, 0}, + { 0x0A36, 0, 0}, + { 0x0A38, 0, 0}, + { 0x0A39, 0, 0}, + { 0x0A3C, 0, 0}, + { 0x0A3E, 0x0A42, 1}, + { 0x0A47, 0, 0}, + { 0x0A48, 0, 0}, + { 0x0A4B, 0x0A4D, 1}, + { 0x0A51, 0, 0}, + { 0x0A59, 0x0A5C, 1}, + { 0x0A5E, 0, 0}, + { 0x0A70, 0x0A75, 1}, + { 0x0A81, 0x0A83, 1}, + { 0x0A85, 0x0A8D, 1}, + { 0x0A8F, 0x0A91, 1}, + { 0x0A93, 0x0AA8, 1}, + { 0x0AAA, 0x0AB0, 1}, + { 0x0AB2, 0, 0}, + { 0x0AB3, 0, 0}, + { 0x0AB5, 0x0AB9, 1}, + { 0x0ABC, 0x0AC5, 1}, + { 0x0AC7, 0x0AC9, 1}, + { 0x0ACB, 0x0ACD, 1}, + { 0x0AD0, 0, 0}, + { 0x0AE0, 0x0AE3, 1}, + { 0x0AF1, 0, 0}, + { 0x0B01, 0x0B03, 1}, + { 0x0B05, 0x0B0C, 1}, + { 0x0B0F, 0, 0}, + { 0x0B10, 0, 0}, + { 0x0B13, 0x0B28, 1}, + { 0x0B2A, 0x0B30, 1}, + { 0x0B32, 0, 0}, + { 0x0B33, 0, 0}, + { 0x0B35, 0x0B39, 1}, + { 0x0B3C, 0x0B44, 1}, + { 0x0B47, 0x0B48, 1}, + { 0x0B4B, 0x0B4D, 1}, + { 0x0B56, 0x0B57, 1}, + { 0x0B5C, 0, 0}, + { 0x0B5D, 0, 0}, + { 0x0B5F, 0x0B63, 1}, + { 0x0B70, 0, 0}, + { 0x0B71, 0, 0}, + { 0x0B82, 0, 0}, + { 0x0B83, 0, 0}, + { 0x0B85, 0x0B8A, 1}, + { 0x0B8E, 0x0B90, 1}, + { 0x0B92, 0x0B95, 1}, + { 0x0B99, 0, 0}, + { 0x0B9A, 0, 0}, + { 0x0B9C, 0, 0}, + { 0x0B9E, 0, 0}, + { 0x0B9F, 0, 0}, + { 0x0BA3, 0, 0}, + { 0x0BA4, 0, 0}, + { 0x0BA8, 0x0BAA, 1}, + { 0x0BAE, 0x0BB9, 1}, + { 0x0BBE, 0x0BC2, 1}, + { 0x0BC6, 0x0BC8, 1}, + { 0x0BCA, 0x0BCD, 1}, + { 0x0BD0, 0, 0}, + { 0x0BD7, 0, 0}, + { 0x0BF0, 0x0BFA, 1}, + { 0x0C01, 0x0C03, 1}, + { 0x0C05, 0x0C0C, 1}, + { 0x0C0E, 0x0C10, 1}, + { 0x0C12, 0x0C28, 1}, + { 0x0C2A, 0x0C33, 1}, + { 0x0C35, 0x0C39, 1}, + { 0x0C3D, 0x0C44, 1}, + { 0x0C46, 0x0C48, 1}, + { 0x0C4A, 0x0C4D, 1}, + { 0x0C55, 0x0C56, 1}, + { 0x0C58, 0x0C59, 1}, + { 0x0C60, 0x0C63, 1}, + { 0x0C82, 0x0C83, 1}, + { 0x0C85, 0x0C8C, 1}, + { 0x0C8E, 0x0C90, 1}, + { 0x0C92, 0x0CA8, 1}, + { 0x0CAA, 0x0CB3, 1}, + { 0x0CB5, 0x0CB9, 1}, + { 0x0CBC, 0x0CC4, 1}, + { 0x0CC6, 0x0CC8, 1}, + { 0x0CCA, 0x0CCD, 1}, + { 0x0CD5, 0x0CD6, 1}, + { 0x0CDE, 0, 0}, + { 0x0CE0, 0x0CE3, 1}, + { 0x0CF1, 0, 0}, + { 0x0CF2, 0, 0}, + { 0x0D02, 0x0D03, 1}, + { 0x0D05, 0x0D0C, 1}, + { 0x0D0E, 0x0D10, 1}, + { 0x0D12, 0x0D28, 1}, + { 0x0D2A, 0x0D39, 1}, + { 0x0D3D, 0x0D44, 1}, + { 0x0D46, 0x0D48, 1}, + { 0x0D4A, 0x0D4D, 1}, + { 0x0D57, 0, 0}, + { 0x0D60, 0x0D63, 1}, + { 0x0D79, 0x0D7F, 1}, + { 0x0D82, 0x0D83, 1}, + { 0x0D85, 0x0D96, 1}, + { 0x0D9A, 0x0DB1, 1}, + { 0x0DB3, 0x0DBB, 1}, + { 0x0DBD, 0, 0}, + { 0x0DC0, 0x0DC6, 1}, + { 0x0DCA, 0, 0}, + { 0x0DCF, 0x0DD4, 1}, + { 0x0DD6, 0, 0}, + { 0x0DD8, 0x0DDF, 1}, + { 0x0DF2, 0x0DF4, 1}, + { 0x0E01, 0x0E2E, 1}, + { 0x0E30, 0x0E3A, 1}, + { 0x0E40, 0x0E45, 1}, + { 0x0E47, 0x0E4E, 1}, + { 0x0E81, 0x0E82, 1}, + { 0x0E84, 0, 0}, + { 0x0E87, 0x0E88, 1}, + { 0x0E8A, 0, 0}, + { 0x0E8D, 0, 0}, + { 0x0E94, 0x0E97, 1}, + { 0x0E99, 0x0E9F, 1}, + { 0x0EA1, 0x0EA3, 1}, + { 0x0EA5, 0, 0}, + { 0x0EA7, 0, 0}, + { 0x0EAA, 0x0EAB, 1}, + { 0x0EAD, 0x0EB0, 1}, + { 0x0EB2, 0x0EB3, 1}, + { 0x0EBD, 0, 0}, + { 0x0EC0, 0x0EC4, 1}, + { 0x0EC6, 0, 0}, + { 0x0EDC, 0x0EDD, 1}, + { 0x0F00, 0, 0}, + { 0x0F40, 0x0F47, 1}, + { 0x0F49, 0x0F6C, 1}, + { 0x0F88, 0x0F8B, 1}, + { 0x1000, 0x102A, 1}, + { 0x1050, 0x1055, 1}, + { 0x105A, 0x105D, 1}, + { 0x1061, 0, 0}, + { 0x0165, 0, 0}, + { 0x1066, 0, 0}, + { 0x106E, 0x1070, 1}, + { 0x1075, 0x1081, 1}, + { 0x108E, 0, 0}, + { 0x10A0, 0x10C5, 1}, + { 0x10D0, 0x10FA, 1}, + { 0x10FC, 0, 0}, + { 0x1100, 0x1159, 1}, + { 0x115F, 0x11A2, 1}, + { 0x11A8, 0x11F9, 1}, + { 0x1200, 0x1248, 1}, + { 0x124A, 0x124D, 1}, + { 0x1250, 0x1256, 1}, + { 0x1258, 0, 0}, + { 0x125A, 0x125D, 1}, + { 0x1260, 0x1288, 1}, + { 0x128A, 0x128D, 1}, + { 0x1290, 0x12B0, 1}, + { 0x12B2, 0x12B5, 1}, + { 0x12B8, 0x12BE, 1}, + { 0x12C0, 0, 0}, + { 0x12C2, 0x12C5, 1}, + { 0x12C8, 0x12D6, 1}, + { 0x12D8, 0x1310, 1}, + { 0x1312, 0x1315, 1}, + { 0x1318, 0x135A, 1}, + { 0x1380, 0x138F, 1}, + { 0x13A0, 0x13F4, 1}, + { 0x1401, 0x166C, 1}, + { 0x166F, 0x1676, 1}, + { 0x1681, 0x169A, 1}, + { 0x16A0, 0x16EA, 1}, + { 0x16EE, 0x16F0, 1}, + { 0x1700, 0x170C, 1}, + { 0x170E, 0x1711, 1}, + { 0x1720, 0x1731, 1}, + { 0x1740, 0x1751, 1}, + { 0x1760, 0x176C, 1}, + { 0x176E, 0x1770, 1}, + { 0x1780, 0x17B3, 1}, + { 0x17D7, 0, 0}, + { 0x17DC, 0, 0}, + { 0x1820, 0x1877, 1}, + { 0x1880, 0x18A8, 1}, + { 0x18AA, 0, 0}, + { 0x1900, 0x191C, 1}, + { 0x1946, 0x194F, 1}, + { 0x1950, 0x196D, 1}, + { 0x1970, 0x1974, 1}, + { 0x1980, 0x19A9, 1}, + { 0x19C1, 0x19C7, 1}, + { 0x19D0, 0x19D9, 1}, + { 0x1A00, 0x1A16, 1}, + { 0x1B05, 0x1B33, 1}, + { 0x1B45, 0x1B4B, 1}, + { 0x1B50, 0x1B59, 1}, + { 0x1B83, 0x1BA0, 1}, + { 0x1BAE, 0x1BAF, 1}, + { 0x1C00, 0x1C23, 1}, + { 0x1C4D, 0x1C4F, 1}, + { 0x1C5A, 0x1C7D, 1}, + { 0x1D00, 0x1DBF, 1}, + { 0x1E00, 0x1E9F, 1}, + { 0x1EA0, 0x1EFF, 1}, + { 0x1F00, 0x1F15, 1}, + { 0x1F18, 0x1F1D, 1}, + { 0x1F20, 0x1F45, 1}, + { 0x1F48, 0x1F4D, 1}, + { 0x1F50, 0x1F57, 1}, + { 0x1F59, 0, 0}, + { 0x1F5B, 0, 0}, + { 0x1F5D, 0, 0}, + { 0x1F5F, 0x1F7D, 1}, + { 0x1F80, 0x1FB4, 1}, + { 0x1FB6, 0x1FBC, 1}, + { 0x1FBE, 0, 0}, + { 0x1FC2, 0x1FC4, 1}, + { 0x1FC6, 0x1FCC, 1}, + { 0x1FD0, 0x1FD3, 1}, + { 0x1FD6, 0x1FDB, 1}, + { 0x1FE0, 0x1FEC, 1}, + { 0x1FF2, 0x1FF4, 1}, + { 0x1FF6, 0x1FFC, 1}, + { 0x2071, 0, 0}, + { 0x207F, 0, 0}, + { 0x2090, 0x2094, 1}, + { 0x2102, 0, 0}, + { 0x2107, 0, 0}, + { 0x210A, 0x2113, 1}, + { 0x2115, 0, 0}, + { 0x2119, 0x211D, 1}, + { 0x2124, 0, 0}, + { 0x2126, 0, 0}, + { 0x2128, 0x212D, 1}, + { 0x212F, 0x2139, 1}, + { 0x213C, 0x213F, 1}, + { 0x2145, 0x2149, 1}, + { 0x214E, 0, 0}, + { 0x2160, 0x2188, 1}, + { 0x249C, 0x24E9, 1}, + { 0x2C00, 0x2C2E, 1}, + { 0x2C30, 0x2C5E, 1}, + { 0x2C60, 0x2C6F, 1}, + { 0x2C71, 0x2C7D, 1}, + { 0x2C80, 0x2CE4, 1}, + { 0x2D00, 0x2D25, 1}, + { 0x2D30, 0x2D65, 1}, + { 0x2D6F, 0, 0}, + { 0x2D80, 0x2D96, 1}, + { 0x2DA0, 0x2DA6, 1}, + { 0x2DA8, 0x2DAE, 1}, + { 0x2DB0, 0x2DB6, 1}, + { 0x2DB8, 0x2DBE, 1}, + { 0x2DC0, 0x2DC6, 1}, + { 0x2DC8, 0x2DCE, 1}, + { 0x2DD0, 0x2DD6, 1}, + { 0x2DD8, 0x2DDE, 1}, + { 0x3005, 0x3007, 1}, + { 0x3021, 0x3029, 1}, + { 0x3031, 0x3035, 1}, + { 0x3038, 0x303C, 1}, + { 0x3041, 0x3096, 1}, + { 0x309D, 0x309F, 1}, + { 0x30A1, 0x30FA, 1}, + { 0x30FC, 0x30FF, 1}, + { 0x3105, 0x312D, 1}, + { 0x3131, 0x318E, 1}, + { 0x31A0, 0x31B7, 1}, + { 0x31F0, 0x31FF, 1}, + { 0x3400, 0x4DB5, 1}, + { 0x4E00, 0x9FBB, 1}, + { 0xA000, 0xA48C, 1}, + { 0xA500, 0xA60B, 1}, + { 0xA610, 0xA61F, 1}, + { 0xA62A, 0xA62B, 1}, + { 0xA640, 0xA65F, 1}, + { 0xA662, 0xA66E, 1}, + { 0xA680, 0xA697, 1}, + { 0xA717, 0xA71F, 1}, + { 0xA722, 0xA78C, 1}, + { 0xA7FB, 0xA7FF, 1}, + { 0xA800, 0, 0}, + { 0xA801, 0, 0}, + { 0xA803, 0xA805, 1}, + { 0xA807, 0xA80A, 1}, + { 0xA80C, 0xA822, 1}, + { 0xA840, 0xA873, 1}, + { 0xA882, 0xA8B3, 1}, + { 0xA90A, 0xA92D, 1}, + { 0xA930, 0xA946, 1}, + { 0xAA00, 0xAA28, 1}, + { 0xAA40, 0xAA42, 1}, + { 0xAA44, 0xAA4B, 1}, + { 0xAC00, 0xD7A3, 1}, + { 0xF900, 0xFA2D, 1}, + { 0xFA30, 0xFA6A, 1}, + { 0xFA70, 0xFAD9, 1}, + { 0xFB00, 0xFB06, 1}, + { 0xFB13, 0xFB17, 1}, + { 0xFB1D, 0, 0}, + { 0xFB1F, 0xFB28, 1}, + { 0xFB2A, 0xFB36, 1}, + { 0xFB38, 0xFB3C, 1}, + { 0xFB3E, 0, 0}, + { 0xFB40, 0, 0}, + { 0xFB41, 0, 0}, + { 0xFB43, 0, 0}, + { 0xFB44, 0, 0}, + { 0xFB46, 0xFB4F, 1}, + { 0xFB50, 0xFBB1, 1}, + { 0xFBD3, 0xFD3D, 1}, + { 0xFD50, 0xFD8F, 1}, + { 0xFD92, 0xFDC7, 1}, + { 0xFDF0, 0xFDFB, 1}, + { 0xFE70, 0xFE74, 1}, + { 0xFE76, 0xFEFC, 1}, + { 0xFF21, 0xFF3A, 1}, + { 0xFF41, 0xFF5A, 1}, + { 0xFF66, 0xFFBE, 1}, + { 0xFFC2, 0xFFC7, 1}, + { 0xFFCA, 0xFFCF, 1}, + { 0xFFD2, 0xFFD7, 1}, + { 0xFFDA, 0xFFDC, 1}, + {0x00010000, 0x0001000B, 1}, + {0x0001000D, 0x00010026, 1}, + {0x00010028, 0x0001003A, 1}, + {0x0001003C, 0x0001003D, 1}, + {0x0001003F, 0x0001004D, 1}, + {0x00010050, 0x0001005D, 1}, + {0x00010080, 0x000100FA, 1}, + {0x00010140, 0x00010174, 1}, + {0x00010280, 0x0001029C, 1}, + {0x000102A0, 0x000102D0, 1}, + {0x00010300, 0x0001031E, 1}, + {0x00010330, 0x0001034A, 1}, + {0x00010380, 0x0001039D, 1}, + {0x000103A0, 0x000103C3, 1}, + {0x000103C8, 0x000103CF, 1}, + {0x000103D1, 0x000103D5, 1}, + {0x00010400, 0x0001044F, 1}, + {0x00010450, 0x0001047F, 1}, + {0x00010480, 0x0001049D, 1}, + {0x000104A0, 0x000104A9, 1}, + {0x00010800, 0x00010805, 1}, + {0x00010808, 0, 0}, + {0x0001080A, 0x00010835, 1}, + {0x00010837, 0x00010838, 1}, + {0x0001083C, 0, 0}, + {0x0001083F, 0, 0}, + {0x00010900, 0x00010915, 1}, + {0x00010A00, 0, 0}, + {0x00010A10, 0x00010A13, 1}, + {0x00010A15, 0x00010A17, 1}, + {0x00010A19, 0x00010A33, 1}, + {0x00012000, 0x0001236E, 1}, + {0x00012400, 0x00012462, 1}, + {0x0001D400, 0x0001D454, 1}, + {0x0001D456, 0x0001D49C, 1}, + {0x0001D49E, 0x0001D49F, 1}, + {0x0001D4A2, 0, 0}, + {0x0001D4A5, 0x0001D4A6, 1}, + {0x0001D4A9, 0x0001D4AC, 1}, + {0x0001D4AE, 0x0001D4B9, 1}, + {0x0001D4BB, 0, 0}, + {0x0001D4BD, 0x0001D4C3, 1}, + {0x0001D4C5, 0x0001D505, 1}, + {0x0001D507, 0x0001D50A, 1}, + {0x0001D50D, 0x0001D514, 1}, + {0x0001D516, 0x0001D51C, 1}, + {0x0001D51E, 0x0001D539, 1}, + {0x0001D53B, 0x0001D53E, 1}, + {0x0001D540, 0x0001D544, 1}, + {0x0001D546, 0, 0}, + {0x0001D54A, 0x0001D550, 1}, + {0x0001D552, 0x0001D6A5, 1}, + {0x0001D6A8, 0x0001D6C0, 1}, + {0x0001D6C2, 0x0001D6DA, 1}, + {0x0001D6DC, 0x0001D6FA, 1}, + {0x0001D6FC, 0x0001D714, 1}, + {0x0001D716, 0x0001D734, 1}, + {0x0001D736, 0x0001D74E, 1}, + {0x0001D750, 0x0001D76E, 1}, + {0x0001D770, 0x0001D788, 1}, + {0x0001D78A, 0x0001D7A8, 1}, + {0x0001D7AA, 0x0001D7C2, 1}, + {0x0001D7C4, 0x0001D7CB, 1}, + {0x0001D7CE, 0x0001D7FF, 1}, + {0x00020000, 0x0002A6D6, 1}, + {0x0002F800, 0x0002FA1D, 1}, + { 0x0660, 0x0669, 1}, + { 0x06F0, 0x06F9, 1}, + { 0x0966, 0x096F, 1}, + { 0x09E6, 0x09EF, 1}, + { 0x0A66, 0x0A6F, 1}, + { 0x0AE6, 0x0AEF, 1}, + { 0x0B66, 0x0B6F, 1}, + { 0x0BE6, 0x0BEF, 1}, + { 0x0C66, 0x0C6F, 1}, + { 0x0C78, 0x0C7F, 1}, + { 0x0CE6, 0x0CEF, 1}, + { 0x0D66, 0x0D75, 1}, + { 0x0D70, 0x0D75, 1}, + { 0x0E50, 0x0E59, 1}, + { 0x0ED0, 0x0ED9, 1}, + { 0x0F20, 0x0F29, 1}, + { 0x1040, 0x1049, 1}, + { 0x17E0, 0x17E9, 1}, + { 0x1810, 0x1819, 1}, + { 0x1BB0, 0x1BB9, 1}, + { 0x1C40, 0x1C49, 1}, + { 0x1C50, 0x1C59, 1}, + { 0xA620, 0xA629, 1}, + { 0xA8D0, 0xA8D9, 1}, + { 0xA900, 0xA909, 1}, + { 0xAA50, 0xAA59, 1}, + { 0xFF10, 0xFF19, 1}, + +/* END::UTF8TABLE */ +}; + +static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len) +{ + unsigned char ub = utf8_mblen[*uc]; + + if (!ub || ub > len || ub > 4) { + return 0; + } + + *wc = *uc & utf8_mask[ub]; + + switch (ub) { + case 4: + if ((uc[1] & 0xc0) != 0x80) { + return 0; + } + *wc <<= 6; + *wc += *++uc & 0x3f; + /* no break */ + case 3: + if ((uc[1] & 0xc0) != 0x80) { + return 0; + } + *wc <<= 6; + *wc += *++uc & 0x3f; + /* no break */ + case 2: + if ((uc[1] & 0xc0) != 0x80) { + return 0; + } + *wc <<= 6; + *wc += *++uc & 0x3f; + /* no break */ + case 1: + break; + + default: + return 0; + } + + return ub; +} + +static inline zend_bool isualpha(unsigned ch) +{ + unsigned i = 0, j; + + PHP_HTTP_DUFF(sizeof(utf8_ranges)/sizeof(utf8_range_t), + if (utf8_ranges[i].start == ch) { + return 1; + } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) { + if (utf8_ranges[i].step == 1) { + return 1; + } + for (j = utf8_ranges[i].start; j <= utf8_ranges[i].end; j+= utf8_ranges[i].step) { + if (ch == j) { + return 1; + } + } + return 0; + } + ++i; + ); + return 0; +} + +static inline zend_bool isualnum(unsigned ch) +{ + /* digits */ + if (ch >= 0x30 && ch <= 0x39) { + return 1; + } + return isualpha(ch); +} + +static inline size_t wctoutf16(unsigned short u16[2], unsigned wc) +{ + if (wc > 0x10ffff || (wc >= 0xd800 && wc <= 0xdfff)) { + return 0; + } + + if (wc <= 0xffff) { + u16[0] = (unsigned short) wc; + return 1; + } + + wc -= 0x10000; + u16[0] = (unsigned short) ((wc >> 10) + 0xd800); + u16[1] = (unsigned short) ((wc & 0x3ff) + 0xdc00); + return 2; +} + +static inline size_t utf16towc(unsigned *wc, unsigned short *u16_str, size_t u16_len) +{ + if (u16_len < 1) { + return 0; + } + if (u16_str[0] - 0xd800 >= 0x800) { + *wc = u16_str[0]; + return 1; + } + if (u16_len < 2 || (u16_str[0] & 0xfffffc00) != 0xd800 || (u16_str[1] & 0xfffffc00) != 0xdc00) { + return 0; + } + *wc = (u16_str[0] << 10) + u16_str[1] - 0x35fdc00; + return 2; +} + +#endif /* PHP_HTTP_UTF8_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 --git a/src/php_http_version.c b/src/php_http_version.c new file mode 100644 index 0000000..7adef9d --- /dev/null +++ b/src/php_http_version.c @@ -0,0 +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) +{ + 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) +{ + 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); + } + minor = *ptr - '0'; + if (minor >= 0 && minor <= 9) { + return php_http_version_init(v, major, minor TSRMLS_CC); + } + } + } + } + + php_error_docref(NULL TSRMLS_CC, 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) +{ + *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 --git a/src/php_http_version.h b/src/php_http_version.h new file mode 100644 index 0000000..40b833e --- /dev/null +++ b/src/php_http_version.h @@ -0,0 +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 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 + */ +