#!/usr/bin/php -dphar.readonly=1 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> ë@��-����������x��a:8:{s:6:"header";s:61:"pharext v@PHAREXT_VERSION@ (c) Michael Wallner <mike@php.net>";s:7:"version";s:17:"@PHAREXT_VERSION@";s:4:"name";s:9:"pecl_http";s:4:"date";s:10:"2015-05-08";s:4:"stub";s:21:"pharext_installer.php";s:7:"license";s:1353:"Copyright (c) 2004-2014, Michael Wallner <mike@iworks.at>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ";s:7:"release";s:8:"2.4.4dev";s:4:"type";s:9:"extension";}���pharext/Cli/Args.phpđ��äČLUđ�� “7¶���������pharext/Cli/Command.php��äČLU�� *—Û¶���������pharext/Command.php��äČLU��ætd\¶���������pharext/Exception.phpc��äČLUc��U†Ï{¶���������pharext/ExecCmd.php��äČLU��čl”ʶ���������pharext/Installer.phpÒ��äČLUÒ��WäJ¶���������pharext/Openssl/PrivateKey.phpÁ��äČLUÁ��&æP¶���������pharext/Packager.php$"��äČLU$"��Đá’E¶���������pharext/SourceDir/Basic.phpŻ��äČLUŻ��zZ“¶���������pharext/SourceDir/Git.php��äČLU��b›ćc¶���������pharext/SourceDir/Pecl.php��äČLU��Ż–¶���������pharext/SourceDir.php]��äČLU]��S ‰¶���������pharext/Task/Activate.phpp ��äČLUp ��ŽEE¶���������pharext/Task/Askpass.phpU��äČLUU��‡*¶������ ���pharext/Task/BundleGenerator.php}��äČLU}�� ï`Y¶���������pharext/Task/Cleanup.php���äČLU���ÒZͶ���������pharext/Task/Configure.phpT��äČLUT��}Ëì¶���������pharext/Task/Extract.phpë��äČLUë��Éè›Ő¶���������pharext/Task/GitClone.phpm��äČLUm��óy”@¶���������pharext/Task/Make.phpȘ��äČLUȘ��œç6 ¶���������pharext/Task/PaxFixup.phpŹ��äČLUŹ��y⯶���������pharext/Task/PeclFixup.phpœ��äČLUœ��eùtš¶���������pharext/Task/PharBuild.phpí��äČLUí��ŁD架���������pharext/Task/PharCompress.phpr��äČLUr�� â¶���������pharext/Task/PharRename.phpä��äČLUä��Š[Ț˶���������pharext/Task/PharSign.phpš��äČLUš��ÛșŠi¶���������pharext/Task/Phpize.php��äČLU��ù 2Ѷ���������pharext/Task/StreamFetch.php��äČLU��ˆîs\¶���������pharext/Task.phpw���äČLUw��� ÄIǶ���������pharext/Tempdir.php”��äČLU”��ë–,¶���������pharext/Tempfile.phpă��äČLUă��ï#¶���������pharext/Tempname.php`��äČLU`��<Np¶���������pharext/Version.php@���äČLU@���ÆC딶���������pharext_installer.phpĘ���äČLUĘ���‹pDZ¶���������pharext_packager.phpÔ���äČLUÔ���Ń1ś¶���������pharext_package.php2���äČLU2���vSTÒ¶���������raphf-1.0.5.ext.pharçg�äČLUçg�tTTȶ���������propro-1.0.1.ext.phar€*�äČLU€*�#Vò¶������ ���package.xml†<��äČLU†<��š}Ż¶���������LICENSEI��äČLUI��PkÇ7¶���������CREDITS'���äČLU'���#ŒßC¶���������Exceptions.txtV���äČLUV���ŠoMͶ���������KnownIssues.txt™��äČLU™��ž'h ¶������ ���ThanksTo.txt ��äČLU ��}ÿ.¶������ ���config.m4���äČLU���€Uś%¶������ ���config9.m4pG��äČLUpG��ŰÍ?Œ¶������ ���config.w32=��äČLU=��BR©5¶���������php_http_api.hF��äČLUF��_ ©¶���������php_http_buffer.c/��äČLU/��[š_¶���������php_http_buffer.h7)��äČLU7)��3jɶ������ ���php_http.cÂ��äČLUÂ��Ą"#»¶���������php_http_client.cŠ��äČLUŠ��B(€ ¶���������php_http_client.hș��äČLUș��h„«E¶���������php_http_client_curl.c\I�äČLU\I�ĘÿKô¶���������php_http_client_curl.h��äČLU��nWò"¶���������php_http_client_request.cM(��äČLUM(��àg°š¶���������php_http_client_request.h ��äČLU ��uuW^¶���������php_http_client_response.c`��äČLU`��afÂ[¶���������php_http_client_response.h��äČLU��rŽ°ć¶���������php_http_cookie.cv��äČLUv��™zl¶���������php_http_cookie.h��äČLU��Œ v¶���������php_http_curl.cß ��äČLUß ��.™ßˆ¶���������php_http_curl.hs��äČLUs��Æpwì¶���������php_http_encoding.cۑ��äČLUۑ��Ę„?«¶���������php_http_encoding.h‚��äČLU‚��„àQo¶���������php_http_env.cł\��äČLUł\��+&‘»¶���������php_http_env.hU��äČLUU��\߉ê¶���������php_http_env_request.cœ(��äČLUœ(��Ώ'ò¶���������php_http_env_request.há��äČLUá��Ï(r¶���������php_http_env_response.c“Œ��äČLU“Œ��”†&¶���������php_http_env_response.h ��äČLU ��<:đ‰¶���������php_http_etag.cÙ ��äČLUÙ ��aŁe%¶���������php_http_etag.h1��äČLU1��è+’Ƕ���������php_http_exception.có��äČLUó��·�ζ���������php_http_exception.hÂ��äČLUÂ��’àӀ¶���������php_http_filter.cS0��äČLUS0��·ăƒŃ¶���������php_http_filter.hÒ��äČLUÒ��Ț€›‚¶������ ���php_http.h!��äČLU!��€ËÀ™¶���������php_http_header_parser.c`F��äČLU`F��Sțçî¶���������php_http_header_parser.hô ��äČLUô ��&èA¶���������php_http_header.cŁ3��äČLUŁ3��rŃ,8¶���������php_http_header.há��äČLUá��ÊWĶ���������php_http_info.c��äČLU��œŚ ¶���������php_http_info.h= ��äČLU= �� î]u¶���������php_http_message_body.ck��äČLUk��~���������php_http_message_body.hZ��äČLUZ��źW{¶���������php_http_message.cI�äČLUI�űy"à¶���������php_http_message.h5��äČLU5��‹ŃO¶���������php_http_message_parser.cb��äČLUb��ńçaM¶���������php_http_message_parser.hX��äČLUX��äK¶���������php_http_misc.cç��äČLUç��T*Èd¶���������php_http_misc.h*��äČLU*��ęEą¶���������php_http_negotiate.c†��äČLU†��Y0vV¶���������php_http_negotiate.hj��äČLUj��§Űć¶���������php_http_object.c›��äČLU›��Cí7Q¶���������php_http_object.h­��äČLU­��p%#¶���������php_http_options.cŃ ��äČLUŃ ��ÚAG¶���������php_http_options.hÄ��äČLUÄ��VU08¶���������php_http_params.c©—��äČLU©—��Mzü,¶���������php_http_params.hŠ ��äČLUŠ ��*|„ę¶���������php_http_querystring.c@]��äČLU@]��æzßH¶���������php_http_querystring.hk��äČLUk��1\ń¶���������php_http_response_codes.h!��äČLU!��țöÍk¶���������php_http_url.cPÄ��äČLUPÄ��Ÿ(Âź¶���������php_http_url.hƒ��äČLUƒ��túa|¶���������php_http_utf8.hŸI��äČLUŸI��pOŰ(¶���������php_http_version.cú ��äČLUú ��Cüà¶���������php_http_version.hŰ��äČLUŰ��'U›Q¶���������tests/skipif.inc ��äČLU ��š„Żù¶������&���tests/data/message_r_content_range.txt”���äČLU”���XßüB¶������&���tests/data/message_r_multipart_put.txtö ��äČLUö ��Vóü ¶���������tests/data/message_rr_empty.txtˆ��äČLUˆ��š‰»'¶������'���tests/data/message_rr_empty_chunked.txt;��äČLU;��< UŚ¶������$���tests/data/message_rr_empty_gzip.txt»��äČLU»��.á±B¶������,���tests/data/message_rr_helloworld_chunked.txt<��äČLU<��Z,1ł¶���������tests/data/urls.txt��äČLU��“áÏȘ¶���������tests/helper/cookie.inc?��äČLU?��Ź}»@¶���������tests/helper/http2.crt˜��äČLU˜��ìƒhü¶���������tests/helper/http2.key��äČLU��ٞ…ζ���������tests/helper/pipeline.incü��äČLUü��š?ês¶���������tests/helper/proxy.inc‡��äČLU‡�� ,:v¶���������tests/helper/server.incë ��äČLUë ��H`°¶���������tests/helper/upload.inc��äČLU��XæÎ{¶���������tests/helper/html/index.htmlˆ���äČLUˆ���·Çî¶���������tests/bug61444.phptl��äČLUl��ÿÁZÙ¶���������tests/bug66388.phptç��äČLUç��#ÓŐș¶���������tests/bug66891.phpt.��äČLU.��‘,Ś¶���������tests/bug67932.phpt#��äČLU#��‰lž+¶���������tests/bug69000.phpt��äČLU��Önă¶���������tests/bug69076.phptô���äČLUô���a84!¶���������tests/bug69313.phpt��äČLU��\PQ©¶���������tests/bug69357.phpt7��äČLU7��‹qîH¶���������tests/client001.phpt6��äČLU6��‰p}¶���������tests/client002.phptĘ��äČLUĘ��mìÇő¶���������tests/client003.phpt3��äČLU3��- Fą¶���������tests/client004.phpt0��äČLU0��‚ïóì¶���������tests/client005.phpt6��äČLU6��»Öäi¶���������tests/client006.phptS��äČLUS��’KÁž¶���������tests/client007.phpt‰��äČLU‰��š#\¶���������tests/client008.phpt��äČLU��öBÀ¶���������tests/client009.phptŐ��äČLUŐ��>%R?¶���������tests/client010.phptC��äČLUC�� SȚk¶���������tests/client011.phpt4��äČLU4��5#€ű¶���������tests/client012.phptÄ��äČLUÄ��Đ·łć¶���������tests/client013.phpt��äČLU��“źë˜¶���������tests/client014.phpt&��äČLU&��ęŸáú¶���������tests/client015.phpt��äČLU��9CG¶���������tests/client016.phpta��äČLUa��ńÌJÒ¶���������tests/client017.phpt6��äČLU6��iÚÄ&¶���������tests/client018.phptí��äČLUí��^4U¶���������tests/client019.phpt��äČLU��ĄíÇè¶���������tests/client020.phptś��äČLUś��ĐÛph¶���������tests/client021.phptx ��äČLUx ��ű“趶���������tests/client022.phpt„��äČLU„��Â,Ö¶���������tests/client023.phptö��äČLUö��Ó/mŠ¶���������tests/client024.phptÉ��äČLUÉ��RY0K¶���������tests/client025.phpt��äČLU��žĂÿP¶���������tests/client026.phpt€��äČLU€��Mć2¶���������tests/clientrequest001.phptć��äČLUć��[č.¶���������tests/clientrequest002.phptN��äČLUN��ÿà_î¶���������tests/clientrequest003.phpt��äČLU��`@B&¶���������tests/clientrequest004.phptJ��äČLUJ��BsLž¶���������tests/clientresponse001.phptp��äČLUp��—5J¶���������tests/clientresponse002.phptš��äČLUš��ț_Ń ¶���������tests/clientresponse003.phpto��äČLUo��àDŠ¶���������tests/cookie001.phptŒ��äČLUŒ��ăŽí¶���������tests/cookie002.phpt7��äČLU7��$ Ì©¶���������tests/cookie003.phptę���äČLUę���àŠÚ¶���������tests/cookie004.phpt��äČLU��Gîv^¶���������tests/cookie005.phpt<��äČLU<��iM™«¶���������tests/cookie006.phpt��äČLU��læY€¶���������tests/cookie007.phptO��äČLUO��ŰŽs¶���������tests/cookie008.phpt|��äČLU|��Ûi¶���������tests/cookie009.phptć��äČLUć��XR#¶���������tests/cookie010.phpt€��äČLU€��釃)¶���������tests/cookie011.phpt^��äČLU^��I‘¶���������tests/cookie012.phptk��äČLUk��àkń ¶���������tests/encstream001.phpt‹��äČLU‹��ÍWƎ¶���������tests/encstream002.phptp��äČLUp��€áo¶���������tests/encstream003.phptX��äČLUX��MGTC¶���������tests/encstream004.phpt1��äČLU1��șh暶���������tests/encstream005.phpt«��äČLU«��Ą†¶���������tests/encstream006.phpt@��äČLU@��đć.¶���������tests/encstream007.phptf��äČLUf��6°¶���������tests/encstream008.phpt4��äČLU4��żȚßđ¶���������tests/encstream009.phpt`��äČLU`��öÀ“7¶���������tests/envrequestbody001.phptÍ���äČLUÍ���š#¶¶���������tests/envrequestcookie001.phptĂ��äČLUĂ��ÙĐČE¶���������tests/envrequestfiles001.phptȘ��äČLUȘ��Îć`Ÿ¶���������tests/envrequestfiles002.phpt]��äČLU]�� X’Ƕ���������tests/envrequestform.phpt—��äČLU—��)€ŒÜ¶���������tests/envrequestheader001.phptw��äČLUw��ê )ö¶���������tests/envrequestquery.phpt›��äČLU›��’@Î[¶���������tests/envresponse001.phptŻ��äČLUŻ��QàEä¶���������tests/envresponse002.phptW��äČLUW��  I¶���������tests/envresponse003.phptŻ��äČLUŻ��ä�¶���������tests/envresponse004.phptë��äČLUë��O­eɶ���������tests/envresponse005.phptI��äČLUI��[ߑ¶���������tests/envresponse006.phptŻ��äČLUŻ��ò5¶±¶���������tests/envresponse007.phptŽ��äČLUŽ��"(Ï$¶���������tests/envresponse008.phpt��äČLU��ŸÖ¶���������tests/envresponse009.phpt2��äČLU2��BăŁf¶���������tests/envresponse010.phpt‰��äČLU‰��UÚDž¶���������tests/envresponse011.phpt˜��äČLU˜��#ò_¶���������tests/envresponse012.phptM��äČLUM��œ“XG¶���������tests/envresponse013.phpt��äČLU��lvĆL¶���������tests/envresponse014.phptí��äČLUí��KWò{¶���������tests/envresponse015.phpt��äČLU��S^¶���������tests/envresponse016.phpt ��äČLU ��x¶���������tests/envresponse017.phptĐ��äČLUĐ��CÒ¶���������tests/envresponse018.phptÉ��äČLUÉ��@/N¶���������tests/envresponsebody001.phptì���äČLUì���« —¶���������tests/envresponsebody002.phpt­��äČLU­��kŠĄ¶���������tests/envresponsecodes.phptK��äČLUK��ÎÓëM¶���������tests/envresponsecookie001.phptš��äČLUš��ҋܶ���������tests/envresponseheader001.phptù��äČLUù��œ“ă¶���������tests/envresponseranges001.phptÂ��äČLUÂ�� 5ö¶���������tests/etag001.phpt/ ��äČLU/ ��Ő”9¶���������tests/filterchunked.phptK��äČLUK��ü—Ă6¶���������tests/filterzlib.phpt[��äČLU[��W&“Š¶���������tests/header001.phptŐ���äČLUŐ���9ÍÏ^¶���������tests/header002.phptÒ���äČLUÒ��� vÏ3¶���������tests/header003.phptY��äČLUY��MŁ‡¶���������tests/header004.phpt ��äČLU ��$7ò¶���������tests/header005.phptÆ��äČLUÆ��aߛȚ¶���������tests/header006.phptç��äČLUç��!>Ș¶���������tests/header007.phpt1��äČLU1��·Űđζ���������tests/header008.phptÙ��äČLUÙ��x€¶���������tests/header009.phptÊ��äČLUÊ��8㾒¶���������tests/headerparser001.phpt©��äČLU©��ȘFçă¶���������tests/headerparser002.phptż��äČLUż���S|¶���������tests/headerparser003.phpt��äČLU��KÓ%¶���������tests/info001.phptM��äČLUM��pÛ _¶���������tests/info002.phptû��äČLUû��±ÆQ%¶���������tests/message001.phpt)��äČLU)��Ń·àč¶���������tests/message002.phptĐ��äČLUĐ�� :Š ¶���������tests/message003.phpt/ ��äČLU/ ��§`Ô¶���������tests/message004.phptĐ��äČLUĐ�� fY?¶���������tests/message005.phptš��äČLUš��äĐܶ���������tests/message006.phptÁ��äČLUÁ���ƒû·¶���������tests/message007.phpt��äČLU��Ăëő›¶���������tests/message008.phptï��äČLUï��˜ÿ]¶���������tests/message009.phptŐ��äČLUŐ��N©™é¶���������tests/message010.phpta��äČLUa��"lIȘ¶���������tests/message011.phptˆ��äČLUˆ��9g2ì¶���������tests/message012.phptÈ��äČLUÈ��­|è¶���������tests/message013.phptč��äČLUč��Ščx¶���������tests/message014.phpt!��äČLU!��mU*¶���������tests/message015.phptv��äČLUv��Æà:k¶���������tests/message016.phptŽ��äČLUŽ��Ü㱶���������tests/messagebody001.phpt]��äČLU]��űE\¶���������tests/messagebody002.phptæ���äČLUæ���Ą|ś=¶���������tests/messagebody003.phptŰ��äČLUŰ��Dńßö���������tests/messagebody004.phptű��äČLUű��đÌŸ¶���������tests/messagebody005.phpt©��äČLU©��nYtœ¶���������tests/messagebody006.phpt„��äČLU„��›!V¶���������tests/messagebody007.phptR��äČLUR��fQ” ¶���������tests/messagebody008.phpt=��äČLU=��ωZ¶���������tests/messagebody009.phptő���äČLUő���|#™Ș¶���������tests/messagebody010.phptm��äČLUm��^ ~}¶���������tests/messageparser001.phptT��äČLUT��dV+¶���������tests/messageparser002.phpt”��äČLU”��ȘkG¶���������tests/negotiate001.phptÜ��äČLUÜ��ąà’'¶���������tests/params001.phpt��äČLU��·*…¶���������tests/params002.phpt?��äČLU?��šu[»¶���������tests/params003.phpt3��äČLU3��)Ű ¶���������tests/params004.phpt)��äČLU)��mœż¶���������tests/params005.phptŹ��äČLUŹ��Ûû¶¶���������tests/params006.phptê��äČLUê��”ŐM°¶���������tests/params007.phptB��äČLUB��ôäĂ ¶���������tests/params008.phptQ��äČLUQ��o<‡¶���������tests/params009.phptÄ���äČLUÄ���I[°¶���������tests/params010.phptR��äČLUR��œˆś=¶���������tests/params011.phptŠ��äČLUŠ�� ąGU¶���������tests/params012.phpt!��äČLU!��ć߆Ž¶���������tests/params013.phpt”��äČLU”��"ÙGa¶���������tests/params014.phptö��äČLUö��l.šQ¶���������tests/params015.phpt•��äČLU•��ÿŠo¶���������tests/phpinfo.phptÆ���äČLUÆ���TÌŸl¶���������tests/propertyproxy001.phpt��äČLU�� @ź~¶���������tests/querystring001.phptú ��äČLUú ��ˆ'Ѷ���������tests/querystring002.phptl��äČLUl��*&˜¶���������tests/querystring003.phpt��äČLU��«—‰¶���������tests/serialize001.phpt#��äČLU#��ȎȘ*¶���������tests/url001.phptË��äČLUË��i»‚ ¶���������tests/url002.phpt ��äČLU ��Rù�đ¶���������tests/url003.phptò��äČLUò��0€°Y¶���������tests/url004.phptF��äČLUF��.uɶ���������tests/url005.phptZ��äČLUZ��u·–í¶���������tests/urlparser001.phptò��äČLUò��büN¶���������tests/urlparser002.phpte ��äČLUe ��bdÿ†¶���������tests/urlparser003.phpt§ ��äČLU§ ��]ј¶���������tests/urlparser004.phpt��äČLU��™�ŸȚ¶���������tests/urlparser005.phpt`��äČLU`��"š§ö¶���������tests/urlparser006.phptj��äČLUj��ÎĘê]¶���������tests/urlparser007.phptç��äČLUç��hс¶���������tests/urlparser008.phptE��äČLUE��9ÔŻ­¶���������tests/urlparser009.phpt- ��äČLU- ��à|n¶���������tests/urlparser010.phpt0��äČLU0��—ƒ:¶���������tests/urlparser011.phptŒ��äČLUŒ��tÖ(¶���������tests/version001.phptÔ��äČLUÔ��«Ű¶������<?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array|Traversable $spec */ public function __construct($spec = null) { if (is_array($spec) || $spec instanceof Traversable) { $this->compile($spec); } } /** * Compile the original spec * @param array|Traversable $spec * @return pharext\CliArgs self */ public function compile($spec) { foreach ($spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; $this->orig[] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!strlen($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } public function toArray() { $args = []; foreach ($this->spec as $spec) { $opt = $this->opt($spec[1]); $args[$opt] = $this[$opt]; } return $args; } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; use Phar; if (!function_exists("array_column")) { function array_column(array $array, $col, $idx = null) { $result = []; foreach ($array as $el) { if (isset($idx)) { $result[$el[$idx]] = $el[$col]; } else { $result[] = $el[$col]; } } return $result; } } trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Retrieve metadata of the currently running phar * @param string $key * @return mixed */ public function metadata($key = null) { $running = new Phar(Phar::running(false)); if ($key === "signature") { $sig = $running->getSignature(); return sprintf("%s signature of %s\n%s", $sig["hash_type"], $this->metadata("name"), chunk_split($sig["hash"], 64, "\n")); } $metadata = $running->getMetadata(); if (isset($key)) { return $metadata[$key]; } return $metadata; } /** * Output pharext vX.Y.Z header */ public function header() { if (!headers_sent()) { /* only display header, if we didn't generate any output yet */ printf("%s\n\n", $this->metadata("header")); } } /** * @inheritdoc * @see \pharext\Command::debug() */ public function debug($fmt) { if ($this->args->verbose) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::warn() */ public function warn($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "Warning: $fmt", $arg); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Verbosity * @return boolean */ public function verbosity() { if ($this->args->verbose) { return true; } elseif ($this->args->quiet) { return false; } else { return null; } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Argument error */ const EARGS = 1; /** * Build error */ const EBUILD = 2; /** * Signature error */ const ESIGN = 3; /** * Extract/unpack error */ const EEXTRACT = 4; /** * Install error */ const EINSTALL = 5; /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print debug message * @param string $fmt * @param string ...$args */ public function debug($fmt); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print warning * @param string $fmt * @param string ...$args */ public function warn($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; class Exception extends \Exception { public function __construct($message = null, $code = 0, $previous = null) { if (!isset($message)) { $last_error = error_get_last(); $message = $last_error["message"]; if (!$code) { $code = $last_error["type"]; } } parent::__construct($message, $code, $previous); } } <?php namespace pharext; /** * Execute system command */ class ExecCmd { /** * Sudo command, if the cmd needs escalated privileges * @var string */ private $sudo; /** * Executable of the cmd * @var string */ private $command; /** * Passthrough cmd output * @var bool */ private $verbose; /** * Output of cmd run * @var string */ private $output; /** * Return code of cmd run * @var int */ private $status; /** * @param string $command * @param bool verbose */ public function __construct($command, $verbose = false) { $this->command = $command; $this->verbose = $verbose; } /** * (Re-)set sudo command * @param string $sudo */ public function setSu($sudo = false) { $this->sudo = $sudo; } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param bool $verbose * @return int exit status */ private function suExec($command, $verbose = null) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { $this->status = -1; throw new Exception("Failed to run {$command}"); } $stdout = $pipes[1]; $passwd = 0; $checks = 10; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd < $checks) { $passwd++; if (stristr($data, "password")) { $passwd = $checks + 1; printf("\n%s", $data); continue; } } elseif ($passwd > $checks) { /* new line after pw entry */ printf("\n"); $passwd = $checks; } if ($verbose === null) { print $this->progress($data, 0); } else { if ($verbose) { printf("%s", $data); } $this->output .= $data; } } if ($verbose === null) { $this->progress("", PHP_OUTPUT_HANDLER_FINAL); } return $this->status = proc_close($proc); } /** * Output handler that displays some progress while soaking output * @param string $string * @param int $flags * @return string */ private function progress($string, $flags) { static $counter = 0; static $symbols = ["\\","|","/","-"]; $this->output .= $string; if (false !== strpos($string, "\n")) { ++$counter; } return $flags & PHP_OUTPUT_HANDLER_FINAL ? " \r" : sprintf(" %s\r", $symbols[$counter % 4]); } /** * Run the command * @param array $args * @return \pharext\ExecCmd self * @throws \pharext\Exception */ public function run(array $args = null) { $exec = escapeshellcmd($this->command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->sudo) { $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->verbose); } elseif ($this->verbose) { ob_start(function($s) { $this->output .= $s; return $s; }, 1); passthru($exec, $this->status); ob_end_flush(); } elseif ($this->verbose !== false /* !quiet */) { ob_start([$this, "progress"], 1); passthru($exec . " 2>&1", $this->status); ob_end_flush(); } else { exec($exec ." 2>&1", $output, $this->status); $this->output = implode("\n", $output); } if ($this->status) { throw new Exception("Command {$exec} failed ({$this->status})"); } return $this; } /** * Retrieve exit code of cmd run * @return int */ public function getStatus() { return $this->status; } /** * Retrieve output of cmd run * @return string */ public function getOutput() { return $this->output; } } <?php namespace pharext; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use Phar; use SplObjectStorage; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Show package signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show package license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "name", "Show package name", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "date", "Show package release date", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "release", "Show package release version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } private function extract(Phar $phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; } private function hooks(SplObjectStorage $phars) { $hook = []; foreach ($phars as $phar) { if (isset($phar["pharext_package.php"])) { $sdir = include $phar["pharext_package.php"]; if ($sdir instanceof SourceDir) { $this->args->compile($sdir->getArgs()); $hook[] = $sdir; } } } return $hook; } private function load() { $list = new SplObjectStorage(); $phar = new Phar(Phar::running(false)); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { $dep_phar = new Phar("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } /* the actual ext.phar at last */ $list[$phar] = $temp; return $list; } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { try { /* load the phar(s) */ $list = $this->load(); /* installer hooks */ $hook = $this->hooks($list); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EEXTRACT); } /* standard arg stuff */ $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "name", "date", "license", "release", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } try { /* post process hooks */ foreach ($hook as $sdir) { $sdir->setArgs($this->args); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } /* install packages */ try { foreach ($list as $phar) { $this->info("Installing %s ...\n", basename($phar->getPath())); $this->install($list[$phar]); $this->activate($list[$phar]); $this->info("Successfully installed %s!\n", basename($phar->getPath())); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EINSTALL); } } /** * Phpize + trinity */ private function install($temp) { // phpize $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"}); $phpize->run($this->verbosity()); // configure $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"}); $configure->run($this->verbosity()); // make $make = new Task\Make($temp); $make->run($this->verbosity()); // install $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $install = new Task\Make($temp, ["install"], $sudo); $install->run($this->verbosity()); } private function activate($temp) { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $type = $this->metadata("type") ?: "extension"; $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo); if (!$activate->run($this->verbosity())) { $this->info("Extension already activated ...\n"); } } } <?php namespace pharext\Openssl; use pharext\Exception; class PrivateKey { /** * Private key * @var string */ private $key; /** * Public key * @var string */ private $pub; /** * Read a private key * @param string $file * @param string $password * @throws \pharext\Exception */ function __construct($file, $password) { /* there appears to be a bug with refcount handling of this * resource; when the resource is stored as property, it cannot be * "coerced to a private key" on openssl_sign() later in another method */ $key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($key)) { throw new Exception("Could not load private key"); } openssl_pkey_export($key, $this->key); $this->pub = openssl_pkey_get_details($key)["key"]; } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \pharext\Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) { throw new Exception; } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use pharext\Exception; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["b", "branch", "Checkout this tag/branch", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["E", "zend", "Mark as Zend Extension", CliArgs::OPTIONAL|CliARgs::SINGLE|CliArgs::NOARG], [null, "signature", "Show pharext signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show pharext license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "license", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } try { /* source needs to be evaluated before CliArgs validation, * so e.g. name and version can be overriden and CliArgs * does not complain about missing arguments */ $this->loadSource(); } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } $this->createPackage(); } /** * Download remote source * @param string $source * @return string local source */ private function download($source) { if ($this->args->git) { $task = new Task\GitClone($source, $this->args->branch); } else { /* print newline only once */ $done = false; $task = new Task\StreamFetch($source, function($bytes_pct) use(&$done) { if (!$done) { $this->info(" %3d%% [%s>%s] \r", floor($bytes_pct*100), str_repeat("=", round(50*$bytes_pct)), str_repeat(" ", round(50*(1-$bytes_pct))) ); if ($bytes_pct == 1) { $done = true; printf("\n"); } } }); } $local = $task->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($local); return $local; } /** * Extract local archive * @param stirng $source * @return string extracted directory */ private function extract($source) { try { $task = new Task\Extract($source); $dest = $task->run($this->verbosity()); } catch (\Exception $e) { if (false === strpos($e->getMessage(), "checksum mismatch")) { throw $e; } $dest = (new Task\PaxFixup($source))->run($this->verbosity()); } $this->cleanup[] = new Task\Cleanup($dest); return $dest; } /** * Localize a possibly remote source * @param string $source * @return string local source directory */ private function localize($source) { if (!stream_is_local($source) || ($this->args->git && isset($this->args->branch))) { $source = $this->download($source); $this->cleanup[] = new Task\Cleanup($source); } $source = realpath($source); if (!is_dir($source)) { $source = $this->extract($source); $this->cleanup[] = new Task\Cleanup($source); if (!$this->args->git) { $source = (new Task\PeclFixup($source))->run($this->verbosity()); } } return $source; } /** * Load the source dir * @throws \pharext\Exception */ private function loadSource(){ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($source); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($source); } elseif (is_file("$source/pharext_package.php")) { $this->source = include "$source/pharext_package.php"; } else { $this->source = new SourceDir\Basic($source); } if (!$this->source instanceof SourceDir) { throw new Exception("Unknown source dir $source"); } foreach ($this->source->getPackageInfo() as $key => $val) { $this->args->$key = $val; } } } /** * Creates the extension phar */ private function createPackage() { try { $meta = array_merge($this->metadata(), [ "date" => date("Y-m-d"), "name" => $this->args->name, "release" => $this->args->release, "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))), "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } try { if ($this->args->sign) { $this->info("Using private key to sign phar ...\n"); $pass = (new Task\Askpass)->run($this->verbosity()); $sign = new Task\PharSign($file, $this->args->sign, $pass); $pkey = $sign->run($this->verbosity()); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::ESIGN); } if ($this->args->gzip) { try { $gzip = (new Task\PharCompress($file, Phar::GZ))->run(); $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created gzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } if ($this->args->bzip) { try { $bzip = (new Task\PharCompress($file, Phar::BZ2))->run(); $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created bzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } try { $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created executable phar %s\n", $name); if (isset($pkey)) { $pkey->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\SourceDir; use FilesystemIterator; use IteratorAggregate; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class Basic implements IteratorAggregate, SourceDir { private $path; public function __construct($path) { $this->path = $path; } public function getBaseDir() { return $this->path; } public function getPackageInfo() { return []; } public function getArgs() { return []; } public function setArgs(Args $args) { } public function filter($current, $key, $iterator) { $sub = $current->getSubPath(); if ($sub === ".git" || $sub === ".hg" || $sub === ".svn") { return false; } return true; } public function getIterator() { $rdi = new RecursiveDirectoryIterator($this->path, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rci = new RecursiveCallbackFilterIterator($rdi, [$this, "filter"]); $rii = new RecursiveIteratorIterator($rci); foreach ($rii as $path => $child) { if (!$child->isDir()) { yield $path; } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\Cli\Args; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * @inheritdoc * @return array */ public function getPackageInfo() { return []; } /** * @inheritdoc * @return array */ public function getArgs() { return []; } /** * @inheritdoc */ public function setArgs(Args $args) { } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { /* there may be symlinks, so no realpath here */ yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\Exception; use pharext\SourceDir; use pharext\Tempfile; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * The package.xml * @var string */ private $file; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { if (is_file("$path/package2.xml")) { $sxe = simplexml_load_file($this->file = "$path/package2.xml"); } elseif (is_file("$path/package.xml")) { $sxe = simplexml_load_file($this->file = "$path/package.xml"); } else { throw new Exception("Missing package.xml in $path"); } $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $this->sxe = $sxe; $this->path = realpath($path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Retrieve gathered package info * @return Generator */ public function getPackageInfo() { if (($name = $this->sxe->xpath("/pecl:package/pecl:name"))) { yield "name" => (string) $name[0]; } if (($release = $this->sxe->xpath("/pecl:package/pecl:version/pecl:release"))) { yield "release" => (string) $release[0]; } if ($this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease")) { yield "zend" => true; } } /** * @inheritdoc * @see \pharext\SourceDir::getArgs() */ public function getArgs() { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } } /** * @inheritdoc * @see \pharext\SourceDir::setArgs() */ public function setArgs(Args $args) { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { /* hook */ $temp = tmpfile(); fprintf($temp, "<?php\nreturn new %s(__DIR__);\n", get_class($this)); rewind($temp); yield "pharext_package.php" => $temp; /* deps */ $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield end($glob); } } /* files */ yield realpath($this->file); foreach ($this->sxe->xpath("//pecl:file") as $file) { yield realpath($this->path ."/". $this->dirOf($file) ."/". $file["name"]); } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Source directory interface, which should yield file names to package on traversal */ interface SourceDir extends \Traversable { /** * Retrieve the base directory * @return string */ public function getBaseDir(); /** * Retrieve gathered package info * @return array|Traversable */ public function getPackageInfo(); /** * Provide installer command line args * @return array|Traversable */ public function getArgs(); /** * Process installer command line args * @param \pharext\Cli\Args $args */ public function setArgs(Cli\Args $args); } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; use pharext\Tempfile; /** * PHP INI activation */ class Activate implements Task { /** * @var string */ private $cwd; /** * @var array */ private $inis; /** * @var string */ private $type; /** * @var string */ private $php_config; /** * @var string */ private $sudo; /** * @param string $cwd working directory * @param array $inis custom INI or list of loaded/scanned INI files * @param string $type extension or zend_extension * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 * @param string $sudo sudo command * @throws \pharext\Exception */ public function __construct($cwd, array $inis, $type = "extension", $prefix = null, $common_name = "php", $sudo = null) { $this->cwd = $cwd; $this->type = $type; $this->sudo = $sudo; if (!$this->inis = $inis) { throw new Exception("No PHP INIs given"); } $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->php_config = $cmd; } /** * @param bool $verbose * @return boolean false, if extension was already activated */ public function run($verbose = false) { if ($verbose !== false) { printf("Running INI activation ...\n"); } $extension = basename(current(glob("{$this->cwd}/modules/*.so"))); if ($this->type === "zend_extension") { $pattern = preg_quote((new ExecCmd($this->php_config))->run(["--extension-dir"])->getOutput() . "/$extension", "/"); } else { $pattern = preg_quote($extension, "/"); } foreach ($this->inis as $file) { if ($verbose) { printf("Checking %s ...\n", $file); } $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*{$this->type}\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { return false; } fwrite($temp->getStream(), $line); } } /* not found; append to last processed file, which is the main by default */ if ($verbose) { printf("Activating in %s ...\n", $file); } fprintf($temp->getStream(), $this->type . "=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); // owner transfer $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $cmd = new ExecCmd("chown", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$ugid, $path]); // permission transfer $perm = decoct($stat["mode"] & 0777); $cmd = new ExecCmd("chmod", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$perm, $path]); // rename $cmd = new ExecCmd("mv", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$path, $file]); if ($verbose) { printf("Replaced %s ...\n", $file); } return true; } } <?php namespace pharext\Task; use pharext\Task; /** * Ask password on console */ class Askpass implements Task { /** * @var string */ private $prompt; /** * @param string $prompt */ public function __construct($prompt = "Password:") { $this->prompt = $prompt; } /** * @param bool $verbose * @return string */ public function run($verbose = false) { system("stty -echo"); printf("%s ", $this->prompt); $pass = fgets(STDIN, 1024); printf("\n"); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } } <?php namespace pharext\Task; use pharext\Task; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * List all library files of pharext to bundle with a phar */ class BundleGenerator implements Task { /** * @param bool $verbose * @return Generator */ public function run($verbose = false) { if ($verbose) { printf("Packaging pharext ... \n"); } $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__))); $rii = new RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { if (!$rii->isDot()) { yield $rii->getSubPathname() => $rii->key(); } } } } <?php namespace pharext\Task; use pharext\Task; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * Recursively cleanup FS entries */ class Cleanup implements Task { /** * @var string */ private $rm; public function __construct($rm) { $this->rm = $rm; } /** * @param bool $verbose */ public function run($verbose = false) { if ($verbose) { printf("Cleaning up %s ...\n", $this->rm); } if ($this->rm instanceof Tempfile) { unset($this->rm); } elseif (is_dir($this->rm)) { $rdi = new RecursiveDirectoryIterator($this->rm, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST); foreach ($rii as $path => $child) { if ($child->isDir()) { rmdir($path); } else { unlink($path); } } rmdir($this->rm); } else { @unlink($this->rm); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Runs extension's configure */ class Configure implements Task { /** * @var array */ private $args; /** * @var string */ private $cwd; /** * @param string $cwd working directory * @param array $args configure args * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 */ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->args = ["--with-php-config=$cmd"]; if ($args) { $this->args = array_merge($this->args, $args); } } public function run($verbose = false) { if ($verbose !== false) { printf("Running ./configure ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("./configure", $verbose); $cmd->run($this->args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Task; use pharext\Tempdir; use Phar; use PharData; /** * Extract a package archive */ class Extract implements Task { /** * @var Phar(Data) */ private $source; /** * @param mixed $source archive location */ public function __construct($source) { if ($source instanceof Phar || $source instanceof PharData) { $this->source = $source; } else { $this->source = new PharData($source); } } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Task; use pharext\Tempdir; /** * Clone a git repo */ class GitClone implements Task { /** * @var string */ private $source; /** * @var string */ private $branch; /** * @param string $source git repo location */ public function __construct($source, $branch = null) { $this->source = $source; $this->branch = $branch; } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $local = new Tempdir("gitclone"); $cmd = new ExecCmd("git", $verbose); if (strlen($this->branch)) { $cmd->run(["clone", "--depth", 1, "--branch", $this->branch, $this->source, $local]); } else { $cmd->run(["clone", $this->source, $local]); } return $local; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Exception; use pharext\Task; /** * Run make in the source dir */ class Make implements Task { /** * @var string */ private $cwd; /** * @var array */ private $args; /** * @var string */ private $sudo; /** * * @param string $cwd working directory * @param array $args make's arguments * @param string $sudo sudo command */ public function __construct($cwd, array $args = null, $sudo = null) { $this->cwd = $cwd; $this->sudo = $sudo; $this->args = $args; } /** * * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running make"); if ($this->args) { foreach ($this->args as $arg) { printf(" %s", $arg); } } printf(" ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("make", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $args = $this->args; if (!$verbose) { $args = array_merge((array) $args, ["-s"]); } $cmd->run($args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; class PaxFixup implements Task { private $source; public function __construct($source) { $this->source = $source; } private function openArchive($source) { $hdr = file_get_contents($source, false, null, 0, 3); if ($hdr === "\x1f\x8b\x08") { $fd = fopen("compress.zlib://$source", "r"); } elseif ($hdr === "BZh") { $fd = fopen("compress.bzip2://$source", "r"); } else { $fd = fopen($source, "r"); } if (!is_resource($fd)) { throw new Exception; } return $fd; } public function run($verbose = false) { if ($verbose !== false) { printf("Fixing up a tarball with global pax header ...\n"); } $temp = new Tempfile("paxfix"); stream_copy_to_stream($this->openArchive($this->source), $temp->getStream(), -1, 1024); $temp->closeStream(); return (new Extract((string) $temp))->run($verbose); } }<?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Fixup package.xml files in an extracted PECL dir */ class PeclFixup implements Task { /** * @var string */ private $source; /** * @param string $source source directory */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return string sanitized source location * @throws \pahrext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Sanitizing PECL dir ...\n"); } $dirs = glob("{$this->source}/*", GLOB_ONLYDIR); $files = array_diff(glob("{$this->source}/*"), $dirs); $check = array_reduce($files, function($r, $v) { return $v && fnmatch("package*.xml", basename($v)); }, true); if (count($dirs) !== 1 || !$check) { throw new Exception("Does not look like an extracted PECL dir: {$this->source}"); } $dest = current($dirs); foreach ($files as $file) { if ($verbose) { printf("Moving %s into %s ...\n", basename($file), basename($dest)); } if (!rename($file, "$dest/" . basename($file))) { throw new Exception; } } return $dest; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\SourceDir; use pharext\Task; use pharext\Tempname; use Phar; /** * Build phar */ class PharBuild implements Task { /** * @var \pharext\SourceDir */ private $source; /** * @var array */ private $meta; /** * @var bool */ private $readonly; /** * @param SourceDir $source extension source directory * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { $this->source = $source; $this->meta = $meta; $this->readonly = $readonly; } /** * @param bool $verbose * @return \pharext\Tempname * @throws \pharext\Exception */ public function run($verbose = false) { /* Phar::compress() and ::convert*() use strtok("."), ugh! * so, be sure to not use any other dots in the filename * except for .phar */ $temp = new Tempname("", "-pharext.phar"); $phar = new Phar($temp); $phar->startBuffering(); if ($this->meta) { $phar->setMetadata($this->meta); if (isset($this->meta["stub"])) { $phar->setDefaultStub($this->meta["stub"]); $phar->setStub("#!/usr/bin/php -dphar.readonly=" . intval($this->readonly) ."\n". $phar->getStub()); } } $phar->buildFromIterator((new Task\BundleGenerator)->run()); if ($this->source) { if ($verbose) { $bdir = $this->source->getBaseDir(); $blen = strlen($bdir); foreach ($this->source as $index => $file) { if (is_resource($file)) { printf("Packaging %s ...\n", $index); $phar[$index] = $file; } else { printf("Packaging %s ...\n", $index = trim(substr($file, $blen), "/")); $phar->addFile($file, $index); } } } else { $phar->buildFromIterator($this->source, $this->source->getBaseDir()); } } $phar->stopBuffering(); if (!chmod($temp, fileperms($temp) | 0111)) { throw new Exception; } return $temp; } }<?php namespace pharext\Task; use pharext\Task; use Phar; /** * Clone a compressed copy of a phar */ class PharCompress implements Task { /** * @var string */ private $file; /** * @var Phar */ private $package; /** * @var int */ private $encoding; /** * @var string */ private $extension; /** * @param string $file path to the original phar * @param int $encoding Phar::GZ or Phar::BZ2 */ public function __construct($file, $encoding) { $this->file = $file; $this->package = new Phar($file); $this->encoding = $encoding; switch ($encoding) { case Phar::GZ: $this->extension = ".gz"; break; case Phar::BZ2: $this->extension = ".bz2"; break; } } /** * @param bool $verbose * @return string */ public function run($verbose = false) { if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } $phar = $this->package->compress($this->encoding); $meta = $phar->getMetadata(); if (isset($meta["stub"])) { /* drop shebang */ $phar->setDefaultStub($meta["stub"]); } return $this->file . $this->extension; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Rename the phar archive */ class PharRename implements Task { /** * @var string */ private $phar; /** * @var string */ private $dest; /** * @var string */ private $name; /** * @param string $phar path to phar * @param string $dest destination dir * @param string $name package name */ public function __construct($phar, $dest, $name) { $this->phar = $phar; $this->dest = $dest; $this->name = $name; } /** * @param bool $verbose * @return string path to renamed phar * @throws \pharext\Exception */ public function run($verbose = false) { $extension = substr(strstr($this->phar, "-pharext.phar"), 8); $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension); if ($verbose) { printf("Renaming %s to %s ...\n", basename($this->phar), basename($name)); } if (!rename($this->phar, $name)) { throw new Exception; } return $name; } } <?php namespace pharext\Task; use pharext\Openssl; use pharext\Task; use Phar; /** * Sign the phar with a private key */ class PharSign implements Task { /** * @var Phar */ private $phar; /** * @var \pharext\Openssl\PrivateKey */ private $pkey; /** * * @param mixed $phar phar instance or path to phar * @param string $pkey path to private key * @param string $pass password for the private key */ public function __construct($phar, $pkey, $pass) { if ($phar instanceof Phar || $phar instanceof PharData) { $this->phar = $phar; } else { $this->phar = new Phar($phar); } $this->pkey = new Openssl\PrivateKey($pkey, $pass); } /** * @param bool $verbose * @return \pharext\Openssl\PrivateKey */ public function run($verbose = false) { if ($verbose) { printf("Signing %s ...\n", basename($this->phar->getPath())); } $this->pkey->sign($this->phar); return $this->pkey; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Run phpize in the extension source directory */ class Phpize implements Task { /** * @var string */ private $phpize; /** * * @var string */ private $cwd; /** * @param string $cwd working directory * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP program common name, e.g. php5 */ public function __construct($cwd, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "ize"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->phpize = $cmd; } /** * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running %s ...\n", $this->phpize); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd($this->phpize, $verbose); $cmd->run(); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; /** * Fetch a remote archive */ class StreamFetch implements Task { /** * @var string */ private $source; /** * @var callable */ private $progress; /** * @param string $source remote file location * @param callable $progress progress callback */ public function __construct($source, callable $progress) { $this->source = $source; $this->progress = $progress; } private function createStreamContext() { $progress = $this->progress; /* avoid bytes_max bug of older PHP versions */ $maxbytes = 0; return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress, &$maxbytes) { if ($bytes_max > $maxbytes) { $maxbytes = $bytes_max; } switch ($notification) { case STREAM_NOTIFY_CONNECT: $progress(0); break; case STREAM_NOTIFY_PROGRESS: $progress($maxbytes > 0 ? $bytes_cur/$maxbytes : .5); break; case STREAM_NOTIFY_COMPLETED: /* this is sometimes not generated, why? */ $progress(1); break; } }]); } /** * @param bool $verbose * @return \pharext\Task\Tempfile * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $context = $this->createStreamContext(); if (!$remote = fopen($this->source, "r", false, $context)) { throw new Exception; } $local = new Tempfile("remote"); if (!stream_copy_to_stream($remote, $local->getStream())) { throw new Exception; } $local->closeStream(); /* STREAM_NOTIFY_COMPLETED is not generated, see above */ call_user_func($this->progress, 1); return $local; } } <?php namespace pharext; /** * Simple task interface */ interface Task { public function run($verbose = false); } <?php namespace pharext; /** * Create a temporary directory */ class Tempdir extends \SplFileInfo { /** * @param string $prefix prefix to uniqid() * @throws \pharext\Exception */ public function __construct($prefix) { $temp = new Tempname($prefix); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception("Could not create tempdir: ".error_get_last()["message"]); } parent::__construct($temp); } } <?php namespace pharext; /** * Create a new temporary file */ class Tempfile extends \SplFileInfo { /** * @var resource */ private $handle; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension * @throws \pharext\Exception */ public function __construct($prefix, $suffix = ".tmp") { $tries = 0; $omask = umask(077); do { $path = new Tempname($prefix, $suffix); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new Exception("Could not create temporary file"); } parent::__construct($path); } /** * Unlink the file */ public function __destruct() { @unlink($this->getPathname()); } /** * Close the stream */ public function closeStream() { fclose($this->handle); } /** * Retrieve the stream resource * @return resource */ public function getStream() { return $this->handle; } } <?php namespace pharext; use pharext\Exception; /** * A temporary file/directory name */ class Tempname { /** * @var string */ private $name; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension */ public function __construct($prefix, $suffix = null) { $temp = sys_get_temp_dir() . "/pharext-" . posix_getlogin(); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception; } $this->name = $temp ."/". uniqid($prefix) . $suffix; } /** * @return string */ public function __toString() { return (string) $this->name; } } <?php namespace pharext; const VERSION = "@PHAREXT_VERSION@"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); <?php /** * The packager sub-stub for bin/pharext */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $packager = new pharext\Packager(); $packager->run($argc, $argv); <?php return new pharext\SourceDir\Pecl(__DIR__); #!/usr/bin/php -dphar.readonly=1 <?php $web = 'index.php'; if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) { Phar::interceptFileFuncs(); set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path()); Phar::webPhar(null, $web); include 'phar://' . __FILE__ . '/' . Extract_Phar::START; return; } if (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) { Extract_Phar::go(true); $mimes = array( 'phps' => 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "<html>\n <head>\n <title>File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> Ž��������������������pharext_install.php��/ČțT��2;Z+¶���������CREDITS���/ČțT���C”]ȶ���������LICENSEA��/ČțTA��ŸŹJț¶���������Doxyfileț*��/ČțTț*��đdŁ¶������ ���config.m4��/ČțT��©Đ€¶������ ���config.w32Đ���/ČțTĐ���ęÍŽ¶������ ���php_raphf.h~4��/ČțT~4��țut±¶������ ���php_raphf.cÆE��/ČțTÆE��‚G(Ł¶���������tests/http001.phpt��/ČțT�� *ź¶���������tests/http002.phptL��/ČțTL��€ÔïS¶���������tests/http003.phpt^��/ČțT^��ˆp¶���������tests/http004.phpt[��/ČțT[��Y諶���������pharext/Cli/Args.php,��/ČțT,��ËŻa϶���������pharext/Cli/Command.phpÔ ��/ČțTÔ ��™ÀŠY¶���������pharext/Command.php;��/ČțT;��›»Š¶���������pharext/Installer.php#��/ČțT#��r<y¶���������pharext/Openssl/PrivateKey.phpŠ��/ČțTŠ��逝n¶���������pharext/Packager.php(��/ČțT(��¶���������pharext/SourceDir/Git.phpž��/ČțTž��šß;à¶���������pharext/SourceDir/Pecl.phph��/ČțTh��MčS¶���������pharext/SourceDir/Pharext.phpu��/ČțTu��úâ<ܶ���������pharext/SourceDir.phpù��/ČțTù��¶Íßű¶���������pharext/Tempfile.phpœ��/ČțTœ��E/Vt¶���������pharext/Version.php4���/ČțT4���ù»Ü¶���������pharext_installer.phpĘ���/ČțTĘ���‹pDZ¶������<?php /** * Generated by pharext v2.0.0 at 2015-03-10 08:58:58 UTC. */ namespace pharext; use pharext\Cli\Args as CliArgs; return function(Installer $installer) { $args = $installer->getArgs(); $args->compile([[ null, "enable-raphf", "Whether to enable raphf support", CliArgs::OPTARG, "yes" ]]); return function(Installer $installer) { $args = $installer->getArgs(); if (isset($args["enable-raphf"])) { $args->configure = "--enable-raphf=".$args["enable-raphf"]; } }; }; raphf Michael Wallner Copyright (c) 2013, Michael Wallner <mike@php.net>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Doxyfile 1.8.5 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "Resource and persistent handle factory API" PROJECT_NUMBER = PROJECT_BRIEF = "A facility to manage possibly persistent resources with a comprehensible API. "\ "Provides simliar functionality like the zend_list API, but with more flexiblity and freedom." PROJECT_LOGO = raphf.png OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = YES TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = php_raphf.h INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = YES ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = DOXYGEN TSRMLS_C= TSRMLS_D= TSRMLS_CC= TSRMLS_DC= PHP_RAPHF_API= EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = NO COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES PHP_ARG_ENABLE(raphf, whether to enable raphf support, [ --enable-raphf Enable resource and persistent handles factory support]) if test "$PHP_RAPHF" != "no"; then PHP_INSTALL_HEADERS(ext/raphf, php_raphf.h) PHP_NEW_EXTENSION(raphf, php_raphf.c, $ext_shared) fi ARG_ENABLE("raphf", "for raphf support", "no"); if (PHP_RAPHF == "yes") { EXTENSION("raphf", "php_raphf.c"); AC_DEFINE("HAVE_RAPHF", 1); PHP_INSTALL_HEADERS("ext/raphf", "php_raphf.h"); } /* +--------------------------------------------------------------------+ | PECL :: raphf | +--------------------------------------------------------------------+ | 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) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_RAPHF_H #define PHP_RAPHF_H #ifndef DOXYGEN extern zend_module_entry raphf_module_entry; #define phpext_raphf_ptr &raphf_module_entry #define PHP_RAPHF_VERSION "1.0.4" #ifdef PHP_WIN32 # define PHP_RAPHF_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 # define PHP_RAPHF_API extern __attribute__ ((visibility("default"))) #else # define PHP_RAPHF_API extern #endif #ifdef ZTS # include "TSRM.h" #endif #endif /** * A resource constructor. * * @param opaque is the \a data from php_persistent_handle_provide() * @param init_arg is the \a init_arg from php_resource_factory_init() * @return the created (persistent) handle */ typedef void *(*php_resource_factory_handle_ctor_t)(void *opaque, void *init_arg TSRMLS_DC); /** * The copy constructor of a resource. * * @param opaque the factory's data * @param handle the (persistent) handle to copy */ typedef void *(*php_resource_factory_handle_copy_t)(void *opaque, void *handle TSRMLS_DC); /** * The destructor of a resource. * * @param opaque the factory's data * @param handle the handle to destroy */ typedef void (*php_resource_factory_handle_dtor_t)(void *opaque, void *handle TSRMLS_DC); /** * The resource ops consisting of a ctor, a copy ctor and a dtor. * * Define this ops and register them with php_persistent_handle_provide() * in MINIT. */ typedef struct php_resource_factory_ops { /** The resource constructor */ php_resource_factory_handle_ctor_t ctor; /** The resource's copy constructor */ php_resource_factory_handle_copy_t copy; /** The resource's destructor */ php_resource_factory_handle_dtor_t dtor; } php_resource_factory_ops_t; /** * The resource factory. */ typedef struct php_resource_factory { /** The resource ops */ php_resource_factory_ops_t fops; /** Opaque user data */ void *data; /** User data destructor */ void (*dtor)(void *data); /** How often this factory is referenced */ unsigned refcount; } php_resource_factory_t; /** * Initialize a resource factory. * * If you register a \a dtor for a resource factory used with a persistent * handle provider, be sure to call php_persistent_handle_cleanup() for your * registered provider in MSHUTDOWN, else the dtor will point to no longer * available memory if the extension has already been unloaded. * * @param f the factory to initialize; if NULL allocated on the heap * @param fops the resource ops to assign to the factory * @param data opaque user data; may be NULL * @param dtor a destructor for the data; may be NULL * @return \a f or an allocated resource factory */ PHP_RAPHF_API php_resource_factory_t *php_resource_factory_init( php_resource_factory_t *f, php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *data)); /** * Increase the refcount of the resource factory. * * @param rf the resource factory * @return the new refcount */ PHP_RAPHF_API unsigned php_resource_factory_addref(php_resource_factory_t *rf); /** * Destroy the resource factory. * * If the factory's refcount reaches 0, the \a dtor for \a data is called. * * @param f the resource factory */ PHP_RAPHF_API void php_resource_factory_dtor(php_resource_factory_t *f); /** * Destroy and free the resource factory. * * Calls php_resource_factory_dtor() and frees \ĂŠ f if the factory's refcount * reached 0. * * @param f the resource factory */ PHP_RAPHF_API void php_resource_factory_free(php_resource_factory_t **f); /** * Construct a resource by the resource factory \a f * * @param f the resource factory * @param init_arg for the resource constructor * @return the new resource */ PHP_RAPHF_API void *php_resource_factory_handle_ctor(php_resource_factory_t *f, void *init_arg TSRMLS_DC); /** * Create a copy of the resource \a handle * * @param f the resource factory * @param handle the resource to copy * @return the copy */ PHP_RAPHF_API void *php_resource_factory_handle_copy(php_resource_factory_t *f, void *handle TSRMLS_DC); /** * Destroy (and free) the resource * * @param f the resource factory * @param handle the resource to destroy */ PHP_RAPHF_API void php_resource_factory_handle_dtor(php_resource_factory_t *f, void *handle TSRMLS_DC); /** * Persistent handles storage */ typedef struct php_persistent_handle_list { /** Storage of free resources */ HashTable free; /** Count of acquired resources */ ulong used; } php_persistent_handle_list_t; /** * Definition of a persistent handle provider. * Holds a resource factory an a persistent handle list. */ typedef struct php_persistent_handle_provider { /** * The list of free handles. * Hash of "ident" => array(handles) entries. Persistent handles are * acquired out of this list. */ php_persistent_handle_list_t list; /** * The resource factory. * New handles are created by this factory. */ php_resource_factory_t rf; } php_persistent_handle_provider_t; typedef struct php_persistent_handle_factory php_persistent_handle_factory_t; /** * Wakeup the persistent handle on re-acquisition. */ typedef void (*php_persistent_handle_wakeup_t)( php_persistent_handle_factory_t *f, void **handle TSRMLS_DC); /** * Retire the persistent handle on release. */ typedef void (*php_persistent_handle_retire_t)( php_persistent_handle_factory_t *f, void **handle TSRMLS_DC); /** * Definition of a persistent handle factory. * * php_persistent_handle_concede() will return a pointer to a * php_persistent_handle_factory if a provider for the \a name_str has * been registered with php_persistent_handle_provide(). */ struct php_persistent_handle_factory { /** The persistent handle provider */ php_persistent_handle_provider_t *provider; /** The persistent handle wakeup routine; may be NULL */ php_persistent_handle_wakeup_t wakeup; /** The persistent handle retire routine; may be NULL */ php_persistent_handle_retire_t retire; /** The ident for which this factory manages resources */ struct { /** ident string */ char *str; /** ident length */ size_t len; } ident; /** Whether it has to be free'd on php_persistent_handle_abandon() */ unsigned free_on_abandon:1; }; /** * Register a persistent handle provider in MINIT. * * Registers a factory provider for \a name_str with \a fops resource factory * ops. Call this in your MINIT. * * A php_resource_factory will be created with \a fops, \a data and \a dtor * and will be stored together with a php_persistent_handle_list in the global * raphf hash. * * A php_persistent_handle_factory can then be retrieved by * php_persistent_handle_concede() at runtime. * * @param name_str the provider name, e.g. "http\Client\Curl" * @param name_len the provider name length, e.g. strlen("http\Client\Curl") * @param fops the resource factory ops * @param data opaque user data * @param dtor \a data destructor * @return SUCCESS/FAILURE */ PHP_RAPHF_API int /* SUCCESS|FAILURE */ php_persistent_handle_provide( const char *name_str, size_t name_len, php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *) TSRMLS_DC); /** * Retrieve a persistent handle factory at runtime. * * If a persistent handle provider has been registered for \a name_str, a new * php_persistent_handle_factory creating resources in the \a ident_str * namespace will be constructed. * * The wakeup routine \a wakeup and the retire routine \a retire will be * assigned to the new php_persistent_handle_factory. * * @param a pointer to a factory; allocated on the heap if NULL * @param name_str the provider name, e.g. "http\Client\Curl" * @param name_len the provider name length, e.g. strlen("http\Client\Curl") * @param ident_str the subsidiary namespace, e.g. "php.net:80" * @param ident_len the subsidiary namespace lenght, e.g. strlen("php.net:80") * @param wakeup any persistent handle wakeup routine * @param retire any persistent handle retire routine * @return \a a or an allocated persistent handle factory */ PHP_RAPHF_API php_persistent_handle_factory_t *php_persistent_handle_concede( php_persistent_handle_factory_t *a, const char *name_str, size_t name_len, const char *ident_str, size_t ident_len, php_persistent_handle_wakeup_t wakeup, php_persistent_handle_retire_t retire TSRMLS_DC); /** * Abandon the persistent handle factory. * * Destroy a php_persistent_handle_factory created by * php_persistent_handle_concede(). If the memory for the factory was allocated, * it will automatically be free'd. * * @param a the persistent handle factory to destroy */ PHP_RAPHF_API void php_persistent_handle_abandon( php_persistent_handle_factory_t *a); /** * Acquire a persistent handle. * * That is, either re-use a resource from the free list or create a new handle. * * If a handle is acquired from the free list, the * php_persistent_handle_factory::wakeup callback will be executed for that * handle. * * @param a the persistent handle factory * @param init_arg the \a init_arg for php_resource_factory_handle_ctor() * @return the acquired resource */ PHP_RAPHF_API void *php_persistent_handle_acquire( php_persistent_handle_factory_t *a, void *init_arg TSRMLS_DC); /** * Release a persistent handle. * * That is, either put it back into the free list for later re-use or clean it * up with php_resource_factory_handle_dtor(). * * If a handle is put back into the free list, the * php_persistent_handle_factory::retire callback will be executed for that * handle. * * @param a the persistent handle factory * @param handle the handle to release */ PHP_RAPHF_API void php_persistent_handle_release( php_persistent_handle_factory_t *a, void *handle TSRMLS_DC); /** * Copy a persistent handle. * * Let the underlying resource factory copy the \a handle. * * @param a the persistent handle factory * @param handle the resource to accrete */ PHP_RAPHF_API void *php_persistent_handle_accrete( php_persistent_handle_factory_t *a, void *handle TSRMLS_DC); /** * Retrieve persistent handle resource factory ops. * * These ops can be used to mask a persistent handle factory as * resource factory itself, so you can transparently use the * resource factory API, both for persistent and non-persistent * ressources. * * Example: * ~~~~~~~~~~~~~~~{.c} * php_resource_factory_t *create_my_rf(const char *persistent_id_str, * size_t persistent_id_len TSRMLS_DC) * { * php_resource_factory_t *rf; * * if (persistent_id_str) { * php_persistent_handle_factory_t *pf; * php_resource_factory_ops_t *ops; * * ops = php_persistent_handle_get_resource_factory_ops(); * * pf = php_persistent_handle_concede(NULL, "my", 2, * persistent_id_str, persistent_id_len, NULL, NULL TSRMLS_CC); * * rf = php_resource_factory_init(NULL, ops, pf, php_persistent_handle_abandon); * } else { * rf = php_resource_factory_init(NULL, &myops, NULL, NULL); * } * return rf; * } * ~~~~~~~~~~~~~~~ */ PHP_RAPHF_API php_resource_factory_ops_t * php_persistent_handle_get_resource_factory_ops(void); /** * Clean persistent handles up. * * Destroy persistent handles of provider \a name_str and in subsidiary * namespace \a ident_str. * * If \a name_str is NULL, all persistent handles of all providers with a * matching \a ident_str will be cleaned up. * * If \a ident_str is NULL all persistent handles of the provider will be * cleaned up. * * Ergo, if both, \a name_str and \a ident_str are NULL, then all * persistent handles will be cleaned up. * * You must call this in MSHUTDOWN, if your resource factory ops hold a * registered php_resource_factory::dtor, else the dtor will point to * memory not any more available if the extension has already been unloaded. * * @param name_str the provider name; may be NULL * @param name_len the provider name length * @param ident_str the subsidiary namespace name; may be NULL * @param ident_len the subsidiary namespace name length */ PHP_RAPHF_API void php_persistent_handle_cleanup(const char *name_str, size_t name_len, const char *ident_str, size_t ident_len TSRMLS_DC); /** * Retrieve statistics about the current process/thread's persistent handles. * * @return a HashTable like: * ~~~~~~~~~~~~~~~ * [ * "name" => [ * "ident" => [ * "used" => 1, * "free" => 0, * ] * ] * ] * ~~~~~~~~~~~~~~~ */ PHP_RAPHF_API HashTable *php_persistent_handle_statall(HashTable *ht TSRMLS_DC); #endif /* PHP_RAPHF_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 */ /* +--------------------------------------------------------------------+ | PECL :: raphf | +--------------------------------------------------------------------+ | 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) 2013, Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_raphf.h" struct php_persistent_handle_globals { ulong limit; HashTable hash; }; ZEND_BEGIN_MODULE_GLOBALS(raphf) struct php_persistent_handle_globals persistent_handle; ZEND_END_MODULE_GLOBALS(raphf) #ifdef ZTS # define PHP_RAPHF_G ((zend_raphf_globals *) \ (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(raphf_globals_id)]) #else # define PHP_RAPHF_G (&raphf_globals) #endif ZEND_DECLARE_MODULE_GLOBALS(raphf) typedef int STATUS; #ifndef PHP_RAPHF_DEBUG_PHANDLES # define PHP_RAPHF_DEBUG_PHANDLES 0 #endif #if PHP_RAPHF_DEBUG_PHANDLES # undef inline # define inline #endif php_resource_factory_t *php_resource_factory_init(php_resource_factory_t *f, php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *data)) { if (!f) { f = emalloc(sizeof(*f)); } memset(f, 0, sizeof(*f)); memcpy(&f->fops, fops, sizeof(*fops)); f->data = data; f->dtor = dtor; f->refcount = 1; return f; } unsigned php_resource_factory_addref(php_resource_factory_t *rf) { return ++rf->refcount; } void php_resource_factory_dtor(php_resource_factory_t *f) { --f->refcount; if (!f->refcount) { if (f->dtor) { f->dtor(f->data); } } } void php_resource_factory_free(php_resource_factory_t **f) { if (*f) { php_resource_factory_dtor(*f); if (!(*f)->refcount) { efree(*f); *f = NULL; } } } void *php_resource_factory_handle_ctor(php_resource_factory_t *f, void *init_arg TSRMLS_DC) { if (f->fops.ctor) { return f->fops.ctor(f->data, init_arg TSRMLS_CC); } return NULL; } void *php_resource_factory_handle_copy(php_resource_factory_t *f, void *handle TSRMLS_DC) { if (f->fops.copy) { return f->fops.copy(f->data, handle TSRMLS_CC); } return NULL; } void php_resource_factory_handle_dtor(php_resource_factory_t *f, void *handle TSRMLS_DC) { if (f->fops.dtor) { f->fops.dtor(f->data, handle TSRMLS_CC); } } static inline php_persistent_handle_list_t *php_persistent_handle_list_init( php_persistent_handle_list_t *list) { int free_list; if ((free_list = !list)) { list = pemalloc(sizeof(php_persistent_handle_list_t), 1); } list->used = 0; if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) { if (free_list) { pefree(list, 1); } list = NULL; } return list; } static int php_persistent_handle_apply_stat(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_persistent_handle_list_t **list = p; zval *zsubentry, *zentry = va_arg(argv, zval *); MAKE_STD_ZVAL(zsubentry); array_init(zsubentry); add_assoc_long_ex(zsubentry, ZEND_STRS("used"), (*list)->used); add_assoc_long_ex(zsubentry, ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free)); add_assoc_zval_ex(zentry, key->arKey, key->nKeyLength, zsubentry); return ZEND_HASH_APPLY_KEEP; } static int php_persistent_handle_apply_statall(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_persistent_handle_provider_t *provider = p; HashTable *ht = va_arg(argv, HashTable *); zval *zentry; MAKE_STD_ZVAL(zentry); array_init(zentry); zend_hash_apply_with_arguments(&provider->list.free TSRMLS_CC, php_persistent_handle_apply_stat, 1, zentry); zend_symtable_update(ht, key->arKey, key->nKeyLength, &zentry, sizeof(zval *), NULL); return ZEND_HASH_APPLY_KEEP; } static int php_persistent_handle_apply_cleanup_ex(void *pp, void *arg TSRMLS_DC) { php_resource_factory_t *rf = arg; void **handle = pp; #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "DESTROY: %p\n", *handle); #endif php_resource_factory_handle_dtor(rf, *handle TSRMLS_CC); return ZEND_HASH_APPLY_REMOVE; } static int php_persistent_handle_apply_cleanup(void *pp, void *arg TSRMLS_DC) { php_resource_factory_t *rf = arg; php_persistent_handle_list_t **listp = pp; zend_hash_apply_with_argument(&(*listp)->free, php_persistent_handle_apply_cleanup_ex, rf TSRMLS_CC); if ((*listp)->used) { return ZEND_HASH_APPLY_KEEP; } zend_hash_destroy(&(*listp)->free); #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "LSTFREE: %p\n", *listp); #endif pefree(*listp, 1); *listp = NULL; return ZEND_HASH_APPLY_REMOVE; } static inline void php_persistent_handle_list_dtor( php_persistent_handle_list_t *list, php_persistent_handle_provider_t *provider TSRMLS_DC) { #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "LSTDTOR: %p\n", list); #endif zend_hash_apply_with_argument(&list->free, php_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC); zend_hash_destroy(&list->free); } static inline void php_persistent_handle_list_free( php_persistent_handle_list_t **list, php_persistent_handle_provider_t *provider TSRMLS_DC) { php_persistent_handle_list_dtor(*list, provider TSRMLS_CC); #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "LSTFREE: %p\n", *list); #endif pefree(*list, 1); *list = NULL; } static int php_persistent_handle_list_apply_dtor(void *listp, void *provider TSRMLS_DC) { php_persistent_handle_list_free(listp, provider TSRMLS_CC); return ZEND_HASH_APPLY_REMOVE; } static inline php_persistent_handle_list_t *php_persistent_handle_list_find( php_persistent_handle_provider_t *provider, const char *ident_str, size_t ident_len TSRMLS_DC) { php_persistent_handle_list_t **list, *new_list; STATUS rv = zend_symtable_find(&provider->list.free, ident_str, ident_len + 1, (void *) &list); if (SUCCESS == rv) { #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "LSTFIND: %p\n", *list); #endif return *list; } if ((new_list = php_persistent_handle_list_init(NULL))) { rv = zend_symtable_update(&provider->list.free, ident_str, ident_len+1, (void *) &new_list, sizeof(php_persistent_handle_list_t *), (void *) &list); if (SUCCESS == rv) { #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "LSTFIND: %p (new)\n", *list); #endif return *list; } php_persistent_handle_list_free(&new_list, provider TSRMLS_CC); } return NULL; } static int php_persistent_handle_apply_cleanup_all(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_persistent_handle_provider_t *provider = p; const char *ident_str = va_arg(argv, const char *); size_t ident_len = va_arg(argv, size_t); php_persistent_handle_list_t *list; if (ident_str && ident_len) { if ((list = php_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) { zend_hash_apply_with_argument(&list->free, php_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC); } } else { zend_hash_apply_with_argument(&provider->list.free, php_persistent_handle_apply_cleanup, &provider->rf TSRMLS_CC); } return ZEND_HASH_APPLY_KEEP; } static void php_persistent_handle_hash_dtor(void *p) { php_persistent_handle_provider_t *provider; TSRMLS_FETCH(); provider = (php_persistent_handle_provider_t *) p; zend_hash_apply_with_argument(&provider->list.free, php_persistent_handle_list_apply_dtor, provider TSRMLS_CC); zend_hash_destroy(&provider->list.free); php_resource_factory_dtor(&provider->rf); } PHP_RAPHF_API STATUS php_persistent_handle_provide(const char *name_str, size_t name_len, php_resource_factory_ops_t *fops, void *data, void (*dtor)(void *) TSRMLS_DC) { STATUS status = FAILURE; php_persistent_handle_provider_t provider; if (php_persistent_handle_list_init(&provider.list)) { if (php_resource_factory_init(&provider.rf, fops, data, dtor)) { #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "PROVIDE: %p %s\n", PHP_RAPHF_G, name_str); #endif status = zend_symtable_update(&PHP_RAPHF_G->persistent_handle.hash, name_str, name_len+1, (void *) &provider, sizeof(php_persistent_handle_provider_t), NULL); if (SUCCESS != status) { php_resource_factory_dtor(&provider.rf); } } } return status; } php_persistent_handle_factory_t *php_persistent_handle_concede( php_persistent_handle_factory_t *a, const char *name_str, size_t name_len, const char *ident_str, size_t ident_len, php_persistent_handle_wakeup_t wakeup, php_persistent_handle_retire_t retire TSRMLS_DC) { STATUS status = FAILURE; php_persistent_handle_factory_t *free_a = NULL; if (!a) { free_a = a = emalloc(sizeof(*a)); } memset(a, 0, sizeof(*a)); status = zend_symtable_find(&PHP_RAPHF_G->persistent_handle.hash, name_str, name_len+1, (void *) &a->provider); if (SUCCESS == status) { a->ident.str = estrndup(ident_str, ident_len); a->ident.len = ident_len; a->wakeup = wakeup; a->retire = retire; if (free_a) { a->free_on_abandon = 1; } } else { if (free_a) { efree(free_a); } a = NULL; } #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "CONCEDE: %p %p (%s) (%s)\n", PHP_RAPHF_G, a ? a->provider : NULL, name_str, ident_str); #endif return a; } PHP_RAPHF_API void php_persistent_handle_abandon( php_persistent_handle_factory_t *a) { zend_bool f = a->free_on_abandon; #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "ABANDON: %p\n", a->provider); #endif STR_FREE(a->ident.str); memset(a, 0, sizeof(*a)); if (f) { efree(a); } } void *php_persistent_handle_acquire( php_persistent_handle_factory_t *a, void *init_arg TSRMLS_DC) { int key; STATUS rv; ulong index; void **handle_ptr, *handle = NULL; php_persistent_handle_list_t *list; list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len TSRMLS_CC); if (list) { zend_hash_internal_pointer_end(&list->free); key = zend_hash_get_current_key(&list->free, NULL, &index, 0); rv = zend_hash_get_current_data(&list->free, (void *) &handle_ptr); if (HASH_KEY_NON_EXISTANT != key && SUCCESS == rv) { handle = *handle_ptr; if (a->wakeup) { a->wakeup(a, &handle TSRMLS_CC); } zend_hash_index_del(&list->free, index); } else { handle = php_resource_factory_handle_ctor(&a->provider->rf, init_arg TSRMLS_CC); } #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "CREATED: %p\n", *handle); #endif if (handle) { ++a->provider->list.used; ++list->used; } } return handle; } void *php_persistent_handle_accrete( php_persistent_handle_factory_t *a, void *handle TSRMLS_DC) { void *new_handle = NULL; php_persistent_handle_list_t *list; new_handle = php_resource_factory_handle_copy(&a->provider->rf, handle TSRMLS_CC); if (handle) { list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len TSRMLS_CC); if (list) { ++list->used; } ++a->provider->list.used; } return new_handle; } void php_persistent_handle_release( php_persistent_handle_factory_t *a, void *handle TSRMLS_DC) { php_persistent_handle_list_t *list; list = php_persistent_handle_list_find(a->provider, a->ident.str, a->ident.len TSRMLS_CC); if (list) { if (a->provider->list.used >= PHP_RAPHF_G->persistent_handle.limit) { #if PHP_RAPHF_DEBUG_PHANDLES fprintf(stderr, "DESTROY: %p\n", *handle); #endif php_resource_factory_handle_dtor(&a->provider->rf, handle TSRMLS_CC); } else { if (a->retire) { a->retire(a, &handle TSRMLS_CC); } zend_hash_next_index_insert(&list->free, (void *) &handle, sizeof(void *), NULL); } --a->provider->list.used; --list->used; } } void php_persistent_handle_cleanup(const char *name_str, size_t name_len, const char *ident_str, size_t ident_len TSRMLS_DC) { php_persistent_handle_provider_t *provider; php_persistent_handle_list_t *list; STATUS rv; if (name_str && name_len) { rv = zend_symtable_find(&PHP_RAPHF_G->persistent_handle.hash, name_str, name_len+1, (void *) &provider); if (SUCCESS == rv) { if (ident_str && ident_len) { list = php_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC); if (list) { zend_hash_apply_with_argument(&list->free, php_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC); } } else { zend_hash_apply_with_argument(&provider->list.free, php_persistent_handle_apply_cleanup, &provider->rf TSRMLS_CC); } } } else { zend_hash_apply_with_arguments( &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC, php_persistent_handle_apply_cleanup_all, 2, ident_str, ident_len); } } HashTable *php_persistent_handle_statall(HashTable *ht TSRMLS_DC) { if (zend_hash_num_elements(&PHP_RAPHF_G->persistent_handle.hash)) { if (!ht) { ALLOC_HASHTABLE(ht); zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0); } zend_hash_apply_with_arguments( &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC, php_persistent_handle_apply_statall, 1, ht); } else if (ht) { ht = NULL; } return ht; } static php_resource_factory_ops_t php_persistent_handle_resource_factory_ops = { (php_resource_factory_handle_ctor_t) php_persistent_handle_acquire, (php_resource_factory_handle_copy_t) php_persistent_handle_accrete, (php_resource_factory_handle_dtor_t) php_persistent_handle_release }; php_resource_factory_ops_t *php_persistent_handle_get_resource_factory_ops(void) { return &php_persistent_handle_resource_factory_ops; } ZEND_BEGIN_ARG_INFO_EX(ai_raphf_stat_persistent_handles, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_FUNCTION(raphf_stat_persistent_handles) { if (SUCCESS == zend_parse_parameters_none()) { object_init(return_value); if (php_persistent_handle_statall(HASH_OF(return_value) TSRMLS_CC)) { return; } zval_dtor(return_value); } RETURN_FALSE; } ZEND_BEGIN_ARG_INFO_EX(ai_raphf_clean_persistent_handles, 0, 0, 0) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, ident) ZEND_END_ARG_INFO(); static PHP_FUNCTION(raphf_clean_persistent_handles) { char *name_str = NULL, *ident_str = NULL; int name_len = 0, ident_len = 0; if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!", &name_str, &name_len, &ident_str, &ident_len)) { php_persistent_handle_cleanup(name_str, name_len, ident_str, ident_len TSRMLS_CC); } } static const zend_function_entry raphf_functions[] = { ZEND_NS_FENTRY("raphf", stat_persistent_handles, ZEND_FN(raphf_stat_persistent_handles), ai_raphf_stat_persistent_handles, 0) ZEND_NS_FENTRY("raphf", clean_persistent_handles, ZEND_FN(raphf_clean_persistent_handles), ai_raphf_clean_persistent_handles, 0) {0} }; PHP_INI_BEGIN() STD_PHP_INI_ENTRY("raphf.persistent_handle.limit", "-1", PHP_INI_SYSTEM, OnUpdateLong, persistent_handle.limit, zend_raphf_globals, raphf_globals) PHP_INI_END() static HashTable *php_persistent_handles_global_hash; static PHP_GINIT_FUNCTION(raphf) { raphf_globals->persistent_handle.limit = -1; zend_hash_init(&raphf_globals->persistent_handle.hash, 0, NULL, php_persistent_handle_hash_dtor, 1); if (php_persistent_handles_global_hash) { zend_hash_copy(&raphf_globals->persistent_handle.hash, php_persistent_handles_global_hash, NULL, NULL, sizeof(php_persistent_handle_provider_t)); } } static PHP_GSHUTDOWN_FUNCTION(raphf) { zend_hash_destroy(&raphf_globals->persistent_handle.hash); } PHP_MINIT_FUNCTION(raphf) { php_persistent_handles_global_hash = &PHP_RAPHF_G->persistent_handle.hash; REGISTER_INI_ENTRIES(); return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(raphf) { UNREGISTER_INI_ENTRIES(); php_persistent_handles_global_hash = NULL; return SUCCESS; } static int php_persistent_handle_apply_info_ex(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_persistent_handle_list_t **list = p; zend_hash_key *super_key = va_arg(argv, zend_hash_key *); char used[21], free[21]; slprintf(used, sizeof(used), "%u", (*list)->used); slprintf(free, sizeof(free), "%d", zend_hash_num_elements(&(*list)->free)); php_info_print_table_row(4, super_key->arKey, key->arKey, used, free); return ZEND_HASH_APPLY_KEEP; } static int php_persistent_handle_apply_info(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { php_persistent_handle_provider_t *provider = p; zend_hash_apply_with_arguments(&provider->list.free TSRMLS_CC, php_persistent_handle_apply_info_ex, 1, key); return ZEND_HASH_APPLY_KEEP; } PHP_MINFO_FUNCTION(raphf) { php_info_print_table_start(); php_info_print_table_header(2, "Resource and persistent handle factory support", "enabled"); php_info_print_table_row(2, "Extension version", PHP_RAPHF_VERSION); php_info_print_table_end(); php_info_print_table_start(); php_info_print_table_colspan_header(4, "Persistent handles in this " #ifdef ZTS "thread" #else "process" #endif ); php_info_print_table_header(4, "Provider", "Ident", "Used", "Free"); zend_hash_apply_with_arguments( &PHP_RAPHF_G->persistent_handle.hash TSRMLS_CC, php_persistent_handle_apply_info, 0); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } zend_module_entry raphf_module_entry = { STANDARD_MODULE_HEADER, "raphf", raphf_functions, PHP_MINIT(raphf), PHP_MSHUTDOWN(raphf), NULL, NULL, PHP_MINFO(raphf), PHP_RAPHF_VERSION, ZEND_MODULE_GLOBALS(raphf), PHP_GINIT(raphf), PHP_GSHUTDOWN(raphf), NULL, STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ #ifdef COMPILE_DL_RAPHF ZEND_GET_MODULE(raphf) #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 */ --TEST-- pecl/http-v2 - general and stat --SKIPIF-- <?php if (!extension_loaded("http")) { die("skip pecl/http needed"); } if (!class_exists("http\\Client", false)) { die("skip pecl/http-v2 with curl support needed"); } ?> --FILE-- <?php echo "Test\n"; $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); $c = new http\Client("curl", "php.net:80"); do { $c->enqueue(new http\Client\Request("GET", "http://php.net")); } while (count($c) < 3); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); unset($c); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); ?> Done --EXPECTF-- Test array(2) { ["http\Client\Curl"]=> array(0) { } ["http\Client\Curl\Request"]=> array(0) { } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(1) ["free"]=> int(0) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(3) ["free"]=> int(0) } } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(1) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(3) } } } Done --TEST-- pecl/http-v2 - clean with name and id --SKIPIF-- <?php if (!extension_loaded("http")) { die("skip pecl/http needed"); } if (!class_exists("http\\Client", false)) { die("skip pecl/http-v2 with curl support needed"); } ?> --FILE-- <?php echo "Test\n"; $c = new http\Client("curl", "php.net:80"); do { $c->enqueue(new http\Client\Request("GET", "http://php.net")); } while (count($c) < 3); unset($c); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); raphf\clean_persistent_handles("http\\Client\\Curl"); raphf\clean_persistent_handles("http\\Client\\Curl\\Request", "php.net:80"); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); ?> Done --EXPECTF-- Test array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(1) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(3) } } } array(2) { ["http\Client\Curl"]=> array(0) { } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(0) } } } Done --TEST-- pecl/http-v2 - clean with id only --SKIPIF-- <?php if (!extension_loaded("http")) { die("skip pecl/http needed"); } if (!class_exists("http\\Client", false)) { die("skip pecl/http-v2 with curl support needed"); } ?> --FILE-- <?php echo "Test\n"; $c = new http\Client("curl", "php.net:80"); do { $c->enqueue(new http\Client\Request("GET", "http://php.net")); } while (count($c) < 3); unset($c); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); raphf\clean_persistent_handles(null, "php.net:80"); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); ?> Done --EXPECTF-- Test array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(1) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(3) } } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(0) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(0) ["free"]=> int(0) } } } Done --TEST-- pecl/http-v2 - partial clean --SKIPIF-- <?php if (!extension_loaded("http")) { die("skip pecl/http needed"); } if (!class_exists("http\\Client", false)) { die("skip pecl/http-v2 with curl support needed"); } ?> --FILE-- <?php echo "Test\n"; $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); $c = new http\Client("curl", "php.net:80"); $c2 = new http\Client("curl", "php.net:80"); do { $c->enqueue(new http\Client\Request("GET", "http://php.net")); $c2->enqueue(new http\Client\Request("GET", "http://php.net")); } while (count($c) < 3); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); unset($c); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); raphf\clean_persistent_handles(); $h = (array) raphf\stat_persistent_handles(); var_dump(array_intersect_key($h, array_flip(preg_grep("/^http/", array_keys($h))))); ?> Done --EXPECTF-- Test array(2) { ["http\Client\Curl"]=> array(0) { } ["http\Client\Curl\Request"]=> array(0) { } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(2) ["free"]=> int(0) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(6) ["free"]=> int(0) } } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(1) ["free"]=> int(1) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(3) ["free"]=> int(3) } } } array(2) { ["http\Client\Curl"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(1) ["free"]=> int(0) } } ["http\Client\Curl\Request"]=> array(1) { ["php.net:80"]=> array(2) { ["used"]=> int(3) ["free"]=> int(0) } } } Done <?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array $spec */ public function __construct(array $spec = null) { $this->compile($spec); } /** * Compile the original spec * @param array $spec * @return pharext\CliArgs self */ public function compile(array $spec = null) { $this->orig = array_merge($this->orig, (array) $spec); foreach ((array) $spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!isset($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; require_once "pharext/Version.php"; trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Output pharext vX.Y.Z header */ function header() { printf("pharext v%s (c) Michael Wallner <mike@php.net>\n\n", \pharext\VERSION); } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Create temporary file/directory name * @param string $prefix * @param string $suffix */ private function tempname($prefix, $suffix = null) { if (!isset($suffix)) { $suffix = uniqid(); } return sprintf("%s/%s.%s", sys_get_temp_dir(), $prefix, $suffix); } /** * Create a new temp directory * @param string $prefix * @return string */ private function newtemp($prefix) { $temp = $this->tempname($prefix); if (!is_dir($temp)) { if (!mkdir($temp, 0700, true)) { $this->error(null); exit(3); } } return $temp; } /** * rm -r * @param string $dir */ private function rm($dir) { foreach (scandir($dir) as $entry) { if ($entry === "." || $entry === "..") { continue; } elseif (is_dir("$dir/$entry")) { $this->rm("$dir/$entry"); } elseif (!unlink("$dir/$entry")) { $this->error(null); } } if (!rmdir($dir)) { $this->error(null); } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * The temporary directory we should operate in * @var string */ private $tmp; /** * The directory we came from * @var string */ private $cwd; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ]); } /** * Cleanup temp directory */ public function __destruct() { $this->cleanup(); } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $this->cwd = getcwd(); $this->tmp = $this->tempname(basename(Phar::running(false))); $phar = new Phar(Phar::running(false)); foreach ($phar as $entry) { if (fnmatch("*.ext.phar*", $entry->getBaseName())) { $temp = $this->newtemp($entry->getBaseName()); $phar->extractTo($temp, $entry->getFilename(), true); $phars[$temp] = new Phar($temp."/".$entry->getFilename()); } } $phars[$this->tmp] = $phar; foreach ($phars as $phar) { if (isset($phar["pharext_install.php"])) { $callable = include $phar["pharext_install.php"]; if (is_callable($callable)) { $recv[] = $callable($this); } } } $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(1); } if (isset($recv)) { foreach ($recv as $r) { $r($this); } } foreach ($phars as $temp => $phar) { $this->installPackage($phar, $temp); } } /** * Prepares, configures, builds and installs the extension */ private function installPackage(Phar $phar, $temp) { $this->info("Installing %s ... \n", basename($phar->getAlias())); try { $phar->extractTo($temp, null, true); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(3); } if (!chdir($temp)) { $this->error(null); exit(4); } // phpize $this->exec("phpize", $this->php("ize")); // configure $args = ["--with-php-config=". $this->php("-config")]; if ($this->args->configure) { $args = array_merge($args, $this->args->configure); } $this->exec("configure", "./configure", $args); // make if ($this->args->verbose) { $this->exec("make", "make", ["-j3"]); } else { $this->exec("make", "make", ["-j3", "-s"]); } // install if ($this->args->verbose) { $this->exec("install", "make", ["install"], true); } else { $this->exec("install", "make", ["install", "-s"], true); } // activate $this->activate(); // cleanup $this->cleanup($temp); } /** * Perform any cleanups */ private function cleanup($temp = null) { if (!isset($temp)) { $temp = $this->tmp; } if (is_dir($temp)) { chdir($this->cwd); $this->info("Cleaning up %s ...\n", $temp); $this->rm($temp); } } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param string $output * @return int */ private function sudo($command, &$output) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { return -1; } $stdout = $pipes[1]; $passwd = 0; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd++ < 10) { if (stristr($data, "password")) { printf("\n%s", $data); } } $output .= $data; } return proc_close($proc); } /** * Execute a system command * @param string $name pretty name * @param string $command command * @param array $args command arguments * @param bool $sudo whether the command may need escalated privileges */ private function exec($name, $command, array $args = null, $sudo = false) { $exec = escapeshellcmd($command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->args->verbose) { $this->info("Running %s ...\n", $exec); } else { $this->info("Running %s ... ", $name); } if ($sudo && isset($this->args->sudo)) { $retval = $this->sudo(sprintf($this->args->sudo." 2>&1", $exec), $output); } elseif ($this->args->verbose) { passthru($exec ." 2>&1", $retval); } else { exec($exec ." 2>&1", $output, $retval); $output = implode("\n", $output); } if ($retval) { $this->error("Command %s failed with (%s)\n", $command, $retval); if (isset($output) && !$this->args->quiet) { printf("%s\n", $output); } exit(2); } if (!$this->args->verbose) { // we already have a bunch of output $this->info("OK\n"); } } /** * Construct a command from prefix common-name and suffix * @param type $suffix * @return string */ private function php($suffix) { $cmd = $this->args["common-name"] . $suffix; if (isset($this->args->prefix)) { $cmd = $this->args->prefix . "/bin/" . $cmd; } return $cmd; } /** * Activate extension in php.ini */ private function activate() { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $extension = basename(current(glob("modules/*.so"))); $pattern = preg_quote($extension); foreach ($files as $index => $file) { $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*extension\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { // already there $this->info("Extension already activated\n"); return; } fwrite($temp->getStream(), $line); } } // not found, add extension line to the last process file if (isset($temp, $file)) { fprintf($temp->getStream(), "extension=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $this->exec("INI owner transfer", "chown", [$ugid, $path], true); $perm = decoct($stat["mode"] & 0777); $this->exec("INI permission transfer", "chmod", [$perm, $path], true); $this->exec("INI activation", "mv", [$path, $file], true); } } } <?php namespace pharext\Openssl; class PrivateKey { /** * OpenSSL pkey resource * @var resource */ private $key; /** * Read a private key * @param string $file * @param string $password * @throws \Exception */ function __construct($file, $password) { $this->key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($this->key)) { throw new \Exception("Could not load private key"); } } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", openssl_pkey_get_details($this->key)["key"]) || !rename("$file.tmp", $file) ) { throw new \Exception(error_get_last()["message"]); } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Dump signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } if ($this->args["signature"]) { exit($this->signature($prog)); } try { if ($this->args["source"]) { if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($this, $this->args["source"]); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($this, $this->args["source"]); } else { $this->source = new SourceDir\Pharext($this, $this->args["source"]); } } } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(1); } $this->createPackage(); } function signature($prog) { try { $sig = (new Phar(Phar::running(false)))->getSignature(); printf("%s signature of %s\n%s", $sig["hash_type"], $prog, chunk_split($sig["hash"], 64, "\n")); return 0; } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); return 2; } } /** * Traverses all pharext source files to bundle * @return Generator */ private function bundle() { $rdi = new \RecursiveDirectoryIterator(__DIR__); $rii = new \RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { yield "pharext/". $rii->getSubPathname() => $rii->key(); } } private function askpass($prompt = "Password:") { system("stty -echo", $retval); if ($retval) { $this->error("Could not disable echo on the terminal\n"); } printf("%s ", $prompt); $pass = fgets(STDIN, 1024); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } /** * Creates the extension phar */ private function createPackage() { $pkguniq = uniqid(); $pkgtemp = $this->tempname($pkguniq, "phar"); $pkgdesc = "{$this->args->name}-{$this->args->release}"; $this->info("Creating phar %s ...%s", $pkgtemp, $this->args->verbose ? "\n" : " "); try { $package = new Phar($pkgtemp); if ($this->args->sign) { $this->info("\nUsing private key to sign phar ... \n"); $privkey = new Openssl\PrivateKey(realpath($this->args->sign), $this->askpass()); $privkey->sign($package); } $package->startBuffering(); $package->buildFromIterator($this->source, $this->source->getBaseDir()); $package->buildFromIterator($this->bundle(__DIR__)); $package->addFile(__DIR__."/../pharext_installer.php", "pharext_installer.php"); $package->setDefaultStub("pharext_installer.php"); $package->setStub("#!/usr/bin/php -dphar.readonly=1\n".$package->getStub()); $package->stopBuffering(); if (!chmod($pkgtemp, 0777)) { $this->error(null); } elseif ($this->args->verbose) { $this->info("Created executable phar %s\n", $pkgtemp); } else { $this->info("OK\n"); } if ($this->args->gzip) { $this->info("Compressing with gzip ... "); try { $package->compress(Phar::GZ) ->setDefaultStub("pharext_installer.php"); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } if ($this->args->bzip) { $this->info("Compressing with bzip ... "); try { $package->compress(Phar::BZ2) ->setDefaultStub("pharext_installer.php"); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } unset($package); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(4); } foreach (glob($pkgtemp."*") as $pkgtemp) { $pkgfile = str_replace($pkguniq, "{$pkgdesc}.ext", $pkgtemp); $pkgname = $this->args->dest ."/". basename($pkgfile); $this->info("Finalizing %s ... ", $pkgname); if (!rename($pkgtemp, $pkgname)) { $this->error(null); exit(5); } $this->info("OK\n"); if ($this->args->sign && isset($privkey)) { $keyname = $this->args->dest ."/". basename($pkgfile) . ".pubkey"; $this->info("Public Key %s ... ", $keyname); try { $privkey->exportPublicKey($keyname); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s", $e->getMessage()); } } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Command */ private $cmd; /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", $file); } /* there may be symlinks, so no realpath here */ if (!file_exists("$path/$file")) { $this->cmd->error("File %s does not exist in %s\n", $file, $path); } yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Packager */ private $cmd; /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { if (!realpath("$path/package.xml")) { throw new \Exception("Missing package.xml in $path"); } $sxe = simplexml_load_file("$path/package.xml"); $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $args = $cmd->getArgs(); if (!isset($args->name)) { $name = (string) $sxe->xpath("/pecl:package/pecl:name")[0]; foreach ($args->parse(2, ["--name", $name]) as $error) { $cmd->error("%s\n", $error); } } if (!isset($args->release)) { $release = (string) $sxe->xpath("/pecl:package/pecl:version/pecl:release")[0]; foreach ($args->parse(2, ["--release", $release]) as $error) { $cmd->error("%s\n", $error); } } $this->cmd = $cmd; $this->sxe = $sxe; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Render installer hook * @param array $configure * @return string */ private static function loadHook($configure, $dependencies) { return include __DIR__."/../../pharext_install.tpl.php"; } /** * Create installer hook * @return \Generator */ private function generateHooks() { $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield realpath($this->path."/".end($glob)); } else { unset($dependencies[$key]); } } $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); if ($configure) { $fd = tmpfile(); ob_start(function($s) use($fd){ fwrite($fd, $s); return null; }); self::loadHook($configure, $dependencies); ob_end_flush(); rewind($fd); yield "pharext_install.php" => $fd; } } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { foreach ($this->generateHooks() as $file => $hook) { if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", is_string($hook) ? $hook : $file); } yield $file => $hook; } foreach ($this->sxe->xpath("//pecl:file") as $file) { $path = $this->path ."/". $this->dirOf($file) ."/". $file["name"]; if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", $path); } if (!($realpath = realpath($path))) { $this->cmd->error("File %s does not exist", $path); } yield $realpath; } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * A source directory containing pharext_package.php and eventually pharext_install.php */ class Pharext implements \IteratorAggregate, SourceDir { /** * @var pharext\Command */ private $cmd; /** * @var string */ private $path; /** * @var callable */ private $iter; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; $callable = include "$path/pharext_package.php"; if (!is_callable($callable)) { throw new \Exception("Package hook did not return a callable"); } $this->iter = $callable($cmd, $path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { if (!is_callable($this->iter)) { return new Git($this->cmd, $this->path); } return call_user_func($this->iter, $this->cmd, $this->path); } } <?php namespace pharext; /** * Source directory interface */ interface SourceDir extends \Traversable { /** * Read the source directory * * Note: Best practices are for others, but if you want to follow them, do * not put constructors in interfaces. Keep your complaints, I warned you. * * @param Command $cmd * @param string $path */ public function __construct(Command $cmd, $path); /** * Retrieve the base directory * @return string */ public function getBaseDir(); } <?php namespace pharext; class Tempfile extends \SplFileInfo { private $handle; function __construct($prefix) { $tries = 0; $template = sys_get_temp_dir()."/$prefix."; $omask = umask(077); do { $path = $template.uniqid(); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new \Exception("Could not create temporary file"); } parent::__construct($path); } function __destruct() { @unlink($this->getPathname()); } function closeStream() { fclose($this->handle); } function getStream() { return $this->handle; } } <?php namespace pharext; const VERSION = "2.0.0"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); [Ü7�ƒĄ(ŁœOʏ䌁���GBMB#!/usr/bin/php -dphar.readonly=1 <?php $web = 'index.php'; if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) { Phar::interceptFileFuncs(); set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path()); Phar::webPhar(null, $web); include 'phar://' . __FILE__ . '/' . Extract_Phar::START; return; } if (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) { Extract_Phar::go(true); $mimes = array( 'phps' => 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "<html>\n <head>\n <title>File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> ,��������������������pharext_install.php��9ČțT��€J”¶���������CREDITS���9ČțT���uśBζ���������LICENSEA��9ČțTA��ŸŹJț¶���������Doxyfilea*��9ČțTa*��Żd¶������ ���config.m4 ��9ČțT ��đ`˜)¶������ ���config.w32Ö���9ČțTÖ���È5ȶ������ ���php_propro.hk��9ČțTk��.[Đđ¶������ ���php_propro.cC:��9ČțTC:��~ôŽ¶���������tests/001.phpt��9ČțT�� òË ¶���������tests/002.phpt8��9ČțT8��L¶���������pharext/Cli/Args.php,��9ČțT,��ËŻa϶���������pharext/Cli/Command.phpÔ ��9ČțTÔ ��™ÀŠY¶���������pharext/Command.php;��9ČțT;��›»Š¶���������pharext/Installer.php#��9ČțT#��r<y¶���������pharext/Openssl/PrivateKey.phpŠ��9ČțTŠ��逝n¶���������pharext/Packager.php(��9ČțT(��¶���������pharext/SourceDir/Git.phpž��9ČțTž��šß;à¶���������pharext/SourceDir/Pecl.phph��9ČțTh��MčS¶���������pharext/SourceDir/Pharext.phpu��9ČțTu��úâ<ܶ���������pharext/SourceDir.phpù��9ČțTù��¶Íßű¶���������pharext/Tempfile.phpœ��9ČțTœ��E/Vt¶���������pharext/Version.php4���9ČțT4���ù»Ü¶���������pharext_installer.phpĘ���9ČțTĘ���‹pDZ¶������<?php /** * Generated by pharext v2.0.0 at 2015-03-10 08:58:58 UTC. */ namespace pharext; use pharext\Cli\Args as CliArgs; return function(Installer $installer) { $args = $installer->getArgs(); $args->compile([[ null, "enable-propro", "Whether to enable property proxy support", CliArgs::OPTARG, "yes" ]]); return function(Installer $installer) { $args = $installer->getArgs(); if (isset($args["enable-propro"])) { $args->configure = "--enable-propro=".$args["enable-propro"]; } }; }; proproCopyright (c) 2013, Michael Wallner <mike@php.net>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Doxyfile 1.8.5 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "Property proxy API" PROJECT_NUMBER = PROJECT_BRIEF = "A facility to manage extension object properties tied to C-struct members" PROJECT_LOGO = OUTPUT_DIRECTORY = CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = YES TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_PACKAGE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = NO EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_MEMBERS_CTORS_1ST = NO SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_FILES = YES SHOW_NAMESPACES = YES FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = php_propro.h INPUT_ENCODING = UTF-8 FILE_PATTERNS = RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = HTML_COLORSTYLE_HUE = 220 HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO GENERATE_QHP = NO QCH_FILE = QHP_NAMESPACE = org.doxygen.Project QHP_VIRTUAL_FOLDER = doc QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = QHG_LOCATION = GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = YES ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = NO EXTERNAL_SEARCH = NO SEARCHENGINE_URL = SEARCHDATA_FILE = searchdata.xml EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4 EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = DOXYGEN TSRMLS_C= TSRMLS_D= TSRMLS_CC= TSRMLS_DC= PHP_PROPRO_API= EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES DOT_NUM_THREADS = 0 DOT_FONTNAME = Helvetica DOT_FONTSIZE = 10 DOT_FONTPATH = CLASS_GRAPH = NO COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES PHP_ARG_ENABLE(propro, whether to enable property proxy support, [ --enable-propro Enable property proxy support]) if test "$PHP_PROPRO" != "no"; then PHP_INSTALL_HEADERS(ext/propro, php_propro.h) PHP_NEW_EXTENSION(propro, php_propro.c, $ext_shared) fi ARG_ENABLE("propro", "for propro support", "no"); if (PHP_PROPRO == "yes") { EXTENSION("propro", "php_propro.c"); AC_DEFINE("HAVE_PROPRO", 1); PHP_INSTALL_HEADERS("ext/propro", "php_propro.h"); } /* +--------------------------------------------------------------------+ | PECL :: propro | +--------------------------------------------------------------------+ | 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) 2013 Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_PROPRO_H #define PHP_PROPRO_H #ifndef DOXYGEN extern zend_module_entry propro_module_entry; #define phpext_propro_ptr &propro_module_entry #define PHP_PROPRO_VERSION "1.0.1" #ifdef PHP_WIN32 # define PHP_PROPRO_API __declspec(dllexport) #elif defined(__GNUC__) && __GNUC__ >= 4 # define PHP_PROPRO_API extern __attribute__ ((visibility("default"))) #else # define PHP_PROPRO_API extern #endif #ifdef ZTS # include <TSRM/TSRM.h> #endif #endif /** * The internal property proxy. * * Container for the object/array holding the proxied property. */ struct php_property_proxy { /** The container holding the property */ zval *container; /** The name of the proxied property */ char *member_str; /** The length of the name */ size_t member_len; }; typedef struct php_property_proxy php_property_proxy_t; /** * The userland object. * * Return an object instance of php\\PropertyProxy to make your C-struct * member accessible by reference from PHP userland. * * Example: * ~~~~~~~~~~{.c} * static zval *my_read_prop(zval *object, zval *member, int type, zend_literal *key TSRMLS_DC) * { * my_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); * my_prophandler_t *handler; * zval *return_value, *copy = my_cast(IS_STRING, member); * * if (SUCCESS == my_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 { * // * // This is the interesting part * // * php_property_proxy_t *proxy; * zend_object_value proxy_ov; * zend_class_entry *proxy_ce; * * proxy = php_property_proxy_init(object, Z_STRVAL_P(copy), Z_STRLEN_P(copy) TSRMLS_CC); * proxy_ce = php_property_proxy_get_class_entry(); * proxy_ov = php_property_proxy_object_new_ex(proxy_ce, proxy, NULL TSRMLS_CC); * RETVAL_OBJVAL(proxy_ov, 0); * } * } else { * zend_object_handlers *oh = zend_get_std_object_handlers(); * return_value = oh->read_property(object, member, type, key TSRMLS_CC); * } * * zval_ptr_dtor(©); * * return return_value; * } * ~~~~~~~~~~ */ struct php_property_proxy_object { /** The std zend_object */ zend_object zo; /** The object value for easy zval creation */ zend_object_value zv; /** The actual property proxy */ php_property_proxy_t *proxy; /** A reference to any parent property proxy object */ struct php_property_proxy_object *parent; }; typedef struct php_property_proxy_object php_property_proxy_object_t; /** * Create a property proxy * * The property proxy will forward reads and writes to itself to the * proxied property with name \a member_str of \a container. * * @param container the container holding the property * @param member_str the name of the proxied property * @param member_len the length of the name * @return a new property proxy */ PHP_PROPRO_API php_property_proxy_t *php_property_proxy_init(zval *container, const char *member_str, size_t member_len TSRMLS_DC); /** * Destroy and free a property proxy. * * The destruction of the property proxy object calls this. * * @param proxy a pointer to the allocated property proxy */ PHP_PROPRO_API void php_property_proxy_free(php_property_proxy_t **proxy); /** * Get the zend_class_entry of php\\PropertyProxy * @return the class entry pointer */ PHP_PROPRO_API zend_class_entry *php_property_proxy_get_class_entry(void); /** * Instantiate a new php\\PropertyProxy * @param ce the property proxy or derived class entry * @return the zval object value */ PHP_PROPRO_API zend_object_value php_property_proxy_object_new( zend_class_entry *ce TSRMLS_DC); /** * Instantiate a new php\\PropertyProxy with \a proxy * @param ce the property proxy or derived class entry * @param proxy the internal property proxy * @param ptr a pointer to store the resulting property proxy object * @return the zval object value */ PHP_PROPRO_API zend_object_value php_property_proxy_object_new_ex( zend_class_entry *ce, php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC); #endif /* PHP_PROPRO_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 */ /* +--------------------------------------------------------------------+ | PECL :: propro | +--------------------------------------------------------------------+ | 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) 2013 Michael Wallner <mike@php.net> | +--------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <php.h> #include <ext/standard/info.h> #include "php_propro.h" typedef int STATUS; #define DEBUG_PROPRO 0 #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*)) #endif php_property_proxy_t *php_property_proxy_init(zval *container, const char *member_str, size_t member_len TSRMLS_DC) { php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy)); Z_ADDREF_P(container); proxy->container = container; proxy->member_str = estrndup(member_str, member_len); proxy->member_len = member_len; return proxy; } void php_property_proxy_free(php_property_proxy_t **proxy) { if (*proxy) { zval_ptr_dtor(&(*proxy)->container); efree((*proxy)->member_str); efree(*proxy); *proxy = NULL; } } static zend_class_entry *php_property_proxy_class_entry; static zend_object_handlers php_property_proxy_object_handlers; zend_class_entry *php_property_proxy_get_class_entry(void) { return php_property_proxy_class_entry; } zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC) { return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC); } static void php_property_proxy_object_free(void *object TSRMLS_DC) { php_property_proxy_object_t *o = object; #if DEBUG_PROPRO fprintf(stderr, "#PP %p free\n", o); #endif if (o->proxy) { php_property_proxy_free(&o->proxy); } if (o->parent) { zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle, o->parent->zv.handlers TSRMLS_CC); o->parent = NULL; } zend_object_std_dtor((zend_object *) o TSRMLS_CC); efree(o); } zend_object_value php_property_proxy_object_new_ex(zend_class_entry *ce, php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC) { php_property_proxy_object_t *o; o = ecalloc(1, sizeof(*o)); zend_object_std_init((zend_object *) o, ce TSRMLS_CC); object_properties_init((zend_object *) o, ce); if (ptr) { *ptr = o; } o->proxy = proxy; o->zv.handle = zend_objects_store_put(o, NULL, php_property_proxy_object_free, NULL TSRMLS_CC); o->zv.handlers = &php_property_proxy_object_handlers; #if DEBUG_PROPRO fprintf(stderr, "#PP %p init\n", o); #endif return o->zv; } #if DEBUG_PROPRO /* we do not really care about TS when debugging */ static int level = 1; static const char space[] = " "; static const char *inoutstr[] = {"< return",""," > enter"}; static void _walk(php_property_proxy_object_t *obj TSRMLS_DC) { if (obj) { _walk(obj->parent TSRMLS_CC); fprintf(stderr, ".%s", obj->proxy->member_str); } } static void debug_propro(int inout, const char *f, zval *object, zval *offset, zval *value TSRMLS_DC) { php_property_proxy_object_t *obj; obj = zend_object_store_get_object(object TSRMLS_CC); fprintf(stderr, "#PP %p %s %s %s ", obj, &space[sizeof(space)-level], inoutstr[inout+1], f); level += inout; _walk(obj TSRMLS_CC); if (*f++=='d' && *f++=='i' && *f++=='m' ) { char *offset_str = "[]"; zval *o = offset; if (o) { convert_to_string_ex(&o); offset_str = Z_STRVAL_P(o); } fprintf(stderr, ".%s", offset_str); if (o && o != offset) { zval_ptr_dtor(&o); } } if (value) { const char *t[] = { "NULL", "int", "float", "bool", "Array", "Object", "string", "resource", "const", "const Array", "callable" }; fprintf(stderr, " = (%s) ", t[Z_TYPE_P(value)&0xf]); zend_print_flat_zval_r(value TSRMLS_CC); } fprintf(stderr, "\n"); } #else #define debug_propro(l, f, obj, off, val) #endif static zval *get_parent_proxied_value(zval *object TSRMLS_DC); static zval *get_proxied_value(zval *object TSRMLS_DC); static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC); static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC); static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); static void set_proxied_value(zval **object, zval *value TSRMLS_DC); static zval *get_parent_proxied_value(zval *object TSRMLS_DC) { zval *value = NULL; php_property_proxy_object_t *obj; obj = zend_object_store_get_object(object TSRMLS_CC); if (obj->proxy) { if (obj->parent) { zval *parent; MAKE_STD_ZVAL(parent); parent->type = IS_OBJECT; parent->value.obj = obj->parent->zv; zend_objects_store_add_ref_by_handle( obj->parent->zv.handle TSRMLS_CC); value = get_proxied_value(parent TSRMLS_CC); zval_ptr_dtor(&parent); } } return value; } static zval *get_proxied_value(zval *object TSRMLS_DC) { zval **hash_value, *value = NULL; php_property_proxy_object_t *obj; STATUS rv; obj = zend_object_store_get_object(object TSRMLS_CC); debug_propro(1, "get", object, NULL, NULL TSRMLS_CC); if (obj->proxy) { if (obj->parent) { zval *parent_value = get_parent_proxied_value(object TSRMLS_CC); if (parent_value && parent_value != obj->proxy->container) { Z_ADDREF_P(parent_value); zval_ptr_dtor(&obj->proxy->container); obj->proxy->container = parent_value; } } switch (Z_TYPE_P(obj->proxy->container)) { case IS_OBJECT: value = zend_read_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, 0 TSRMLS_CC); break; case IS_ARRAY: rv = zend_symtable_find(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &hash_value); if (SUCCESS == rv) { value = *hash_value; } break; } } debug_propro(-1, "get", object, NULL, value TSRMLS_CC); return value; } static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC) { zval *proxied_value; if ((proxied_value = get_proxied_value(object TSRMLS_CC))) { RETVAL_ZVAL(proxied_value, 1, 0); if (Z_TYPE_P(proxied_value) != type) { convert_to_explicit_type(return_value, type); } return SUCCESS; } return FAILURE; } static void set_proxied_value(zval **object, zval *value TSRMLS_DC) { php_property_proxy_object_t *obj; obj = zend_object_store_get_object(*object TSRMLS_CC); debug_propro(1, "set", *object, NULL, value TSRMLS_CC); if (obj->proxy) { if (obj->parent) { zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC); if (parent_value && parent_value != obj->proxy->container) { Z_ADDREF_P(parent_value); zval_ptr_dtor(&obj->proxy->container); obj->proxy->container = parent_value; } } switch (Z_TYPE_P(obj->proxy->container)) { case IS_OBJECT: zend_update_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, value TSRMLS_CC); break; case IS_ARRAY: Z_ADDREF_P(value); zend_symtable_update(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &value, sizeof(zval *), NULL); break; } if (obj->parent) { zval *zparent; MAKE_STD_ZVAL(zparent); zparent->type = IS_OBJECT; zparent->value.obj = obj->parent->zv; zend_objects_store_add_ref_by_handle( obj->parent->zv.handle TSRMLS_CC); set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC); zval_ptr_dtor(&zparent); } } debug_propro(-1, "set", *object, NULL, NULL TSRMLS_CC); } static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC) { zval *value = NULL; zval *proxied_value; zval *o = offset; debug_propro(1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object, offset, NULL TSRMLS_CC); proxied_value = get_proxied_value(object TSRMLS_CC); convert_to_string_ex(&o); if (BP_VAR_R == type && proxied_value) { if (Z_TYPE_P(proxied_value) == IS_ARRAY) { zval **hash_value; STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value); if (SUCCESS == rv) { Z_ADDREF_PP(hash_value); value = *hash_value; } } } else { php_property_proxy_t *proxy; php_property_proxy_object_t *proxy_obj; if (proxied_value) { convert_to_array(proxied_value); Z_ADDREF_P(proxied_value); } else { MAKE_STD_ZVAL(proxied_value); array_init(proxied_value); set_proxied_value(&object, proxied_value TSRMLS_CC); } proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o), Z_STRLEN_P(o) TSRMLS_CC); zval_ptr_dtor(&proxied_value); MAKE_STD_ZVAL(value); Z_SET_REFCOUNT_P(value, 0); value->type = IS_OBJECT; value->value.obj = php_property_proxy_object_new_ex( php_property_proxy_get_class_entry(), proxy, &proxy_obj TSRMLS_CC); proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC); zend_objects_store_add_ref_by_handle( proxy_obj->parent->zv.handle TSRMLS_CC); } if (o && o != offset) { zval_ptr_dtor(&o); } debug_propro(-1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object, offset, value TSRMLS_CC); return value; } static int has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) { zval *proxied_value; int exists = 0; debug_propro(1, "dim_exists", object, offset, NULL TSRMLS_CC); proxied_value = get_proxied_value(object TSRMLS_CC); if (!proxied_value) { exists = 0; } else { zval *o = offset; convert_to_string_ex(&o); if (Z_TYPE_P(proxied_value) == IS_ARRAY) { zval **zentry; STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &zentry); if (SUCCESS != rv) { exists = 0; } else { if (check_empty) { exists = Z_TYPE_PP(zentry) != IS_NULL; } else { exists = 1; } } } if (o != offset) { zval_ptr_dtor(&o); } } debug_propro(-1, "dim_exists", object, offset, NULL TSRMLS_CC); return exists; } static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) { zval *proxied_value, *o = offset; debug_propro(1, "dim_write", object, offset, value TSRMLS_CC); proxied_value = get_proxied_value(object TSRMLS_CC); if (proxied_value) { convert_to_array(proxied_value); Z_ADDREF_P(proxied_value); } else { MAKE_STD_ZVAL(proxied_value); array_init(proxied_value); } if (Z_REFCOUNT_P(value) > 1) { SEPARATE_ZVAL(&value); } Z_ADDREF_P(value); if (o) { convert_to_string_ex(&o); zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL); } else { zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value, sizeof(zval *), NULL); } if (o && o != offset) { zval_ptr_dtor(&o); } set_proxied_value(&object, proxied_value TSRMLS_CC); debug_propro(-1, "dim_write", object, offset, proxied_value TSRMLS_CC); zval_ptr_dtor(&proxied_value); } static void unset_dimension(zval *object, zval *offset TSRMLS_DC) { zval *proxied_value; debug_propro(1, "dim_unset", object, offset, NULL TSRMLS_CC); proxied_value = get_proxied_value(object TSRMLS_CC); if (proxied_value && Z_TYPE_P(proxied_value) == IS_ARRAY) { zval *o = offset; STATUS rv; convert_to_string_ex(&o); rv = zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1); if (SUCCESS == rv) { set_proxied_value(&object, proxied_value TSRMLS_CC); } if (o != offset) { zval_ptr_dtor(&o); } } debug_propro(-1, "dim_unset", object, offset, proxied_value TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2) ZEND_ARG_INFO(1, object) ZEND_ARG_INFO(0, member) ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(propro, __construct) { zend_error_handling zeh; zval *container, *parent = NULL; char *member_str; int member_len; zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!", &container, &member_str, &member_len, &parent, php_property_proxy_class_entry)) { php_property_proxy_object_t *obj; obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->proxy = php_property_proxy_init(container, member_str, member_len TSRMLS_CC); if (parent) { zend_objects_store_add_ref(parent TSRMLS_CC); obj->parent = zend_object_store_get_object(parent TSRMLS_CC); } } zend_restore_error_handling(&zeh TSRMLS_CC); } static const zend_function_entry php_property_proxy_method_entry[] = { PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC) {0} }; static PHP_MINIT_FUNCTION(propro) { zend_class_entry ce = {0}; INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy", php_property_proxy_method_entry); php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_property_proxy_class_entry->create_object = php_property_proxy_object_new; php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS; memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_property_proxy_object_handlers.set = set_proxied_value; php_property_proxy_object_handlers.get = get_proxied_value; php_property_proxy_object_handlers.cast_object = cast_proxied_value; php_property_proxy_object_handlers.read_dimension = read_dimension; php_property_proxy_object_handlers.write_dimension = write_dimension; php_property_proxy_object_handlers.has_dimension = has_dimension; php_property_proxy_object_handlers.unset_dimension = unset_dimension; return SUCCESS; } PHP_MINFO_FUNCTION(propro) { php_info_print_table_start(); php_info_print_table_header(2, "Property proxy support", "enabled"); php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION); php_info_print_table_end(); } static const zend_function_entry propro_functions[] = { {0} }; zend_module_entry propro_module_entry = { STANDARD_MODULE_HEADER, "propro", propro_functions, PHP_MINIT(propro), NULL, NULL, NULL, PHP_MINFO(propro), PHP_PROPRO_VERSION, STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_PROPRO ZEND_GET_MODULE(propro) #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 */ --TEST-- property proxy --SKIPIF-- <?php if (!extension_loaded("propro")) print "skip"; ?> --FILE-- <?php echo "Test\n"; class c { private $prop; private $anon; function __get($p) { return new php\PropertyProxy($this, $p); } } $c = new c; $p = $c->prop; $a = $c->anon; var_dump($c); $a = 123; echo $a,"\n"; $p["foo"] = 123; $p["bar"]["baz"]["a"]["b"]=987; var_dump($c); ?> DONE --EXPECTF-- Test object(c)#%d (2) { ["prop":"c":private]=> NULL ["anon":"c":private]=> NULL } 123 object(c)#%d (2) { ["prop":"c":private]=> array(2) { ["foo"]=> int(123) ["bar"]=> array(1) { ["baz"]=> array(1) { ["a"]=> array(1) { ["b"]=> int(987) } } } } ["anon":"c":private]=> int(123) } DONE --TEST-- property proxy --SKIPIF-- <?php if (!extension_loaded("propro")) print "skip"; ?> --FILE-- <?php echo "Test\n"; class c { private $storage = array(); function __get($p) { return new php\PropertyProxy($this->storage, $p); } function __set($p, $v) { $this->storage[$p] = $v; } } $c = new c; $c->data["foo"] = 1; var_dump( isset($c->data["foo"]), isset($c->data["bar"]) ); var_dump($c); $c->data[] = 1; $c->data[] = 2; $c->data[] = 3; $c->data["bar"][] = 123; $c->data["bar"][] = 456; var_dump($c); unset($c->data["bar"][0]); var_dump($c); ?> DONE --EXPECTF-- Test bool(true) bool(false) object(c)#%d (1) { ["storage":"c":private]=> array(1) { ["data"]=> array(1) { ["foo"]=> int(1) } } } object(c)#%d (1) { ["storage":"c":private]=> array(1) { ["data"]=> array(5) { ["foo"]=> int(1) [0]=> int(1) [1]=> int(2) [2]=> int(3) ["bar"]=> array(2) { [0]=> int(123) [1]=> int(456) } } } } object(c)#%d (1) { ["storage":"c":private]=> array(1) { ["data"]=> array(5) { ["foo"]=> int(1) [0]=> int(1) [1]=> int(2) [2]=> int(3) ["bar"]=> array(1) { [1]=> int(456) } } } } DONE <?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array $spec */ public function __construct(array $spec = null) { $this->compile($spec); } /** * Compile the original spec * @param array $spec * @return pharext\CliArgs self */ public function compile(array $spec = null) { $this->orig = array_merge($this->orig, (array) $spec); foreach ((array) $spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!isset($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; require_once "pharext/Version.php"; trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Output pharext vX.Y.Z header */ function header() { printf("pharext v%s (c) Michael Wallner <mike@php.net>\n\n", \pharext\VERSION); } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Create temporary file/directory name * @param string $prefix * @param string $suffix */ private function tempname($prefix, $suffix = null) { if (!isset($suffix)) { $suffix = uniqid(); } return sprintf("%s/%s.%s", sys_get_temp_dir(), $prefix, $suffix); } /** * Create a new temp directory * @param string $prefix * @return string */ private function newtemp($prefix) { $temp = $this->tempname($prefix); if (!is_dir($temp)) { if (!mkdir($temp, 0700, true)) { $this->error(null); exit(3); } } return $temp; } /** * rm -r * @param string $dir */ private function rm($dir) { foreach (scandir($dir) as $entry) { if ($entry === "." || $entry === "..") { continue; } elseif (is_dir("$dir/$entry")) { $this->rm("$dir/$entry"); } elseif (!unlink("$dir/$entry")) { $this->error(null); } } if (!rmdir($dir)) { $this->error(null); } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * The temporary directory we should operate in * @var string */ private $tmp; /** * The directory we came from * @var string */ private $cwd; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ]); } /** * Cleanup temp directory */ public function __destruct() { $this->cleanup(); } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $this->cwd = getcwd(); $this->tmp = $this->tempname(basename(Phar::running(false))); $phar = new Phar(Phar::running(false)); foreach ($phar as $entry) { if (fnmatch("*.ext.phar*", $entry->getBaseName())) { $temp = $this->newtemp($entry->getBaseName()); $phar->extractTo($temp, $entry->getFilename(), true); $phars[$temp] = new Phar($temp."/".$entry->getFilename()); } } $phars[$this->tmp] = $phar; foreach ($phars as $phar) { if (isset($phar["pharext_install.php"])) { $callable = include $phar["pharext_install.php"]; if (is_callable($callable)) { $recv[] = $callable($this); } } } $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(1); } if (isset($recv)) { foreach ($recv as $r) { $r($this); } } foreach ($phars as $temp => $phar) { $this->installPackage($phar, $temp); } } /** * Prepares, configures, builds and installs the extension */ private function installPackage(Phar $phar, $temp) { $this->info("Installing %s ... \n", basename($phar->getAlias())); try { $phar->extractTo($temp, null, true); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(3); } if (!chdir($temp)) { $this->error(null); exit(4); } // phpize $this->exec("phpize", $this->php("ize")); // configure $args = ["--with-php-config=". $this->php("-config")]; if ($this->args->configure) { $args = array_merge($args, $this->args->configure); } $this->exec("configure", "./configure", $args); // make if ($this->args->verbose) { $this->exec("make", "make", ["-j3"]); } else { $this->exec("make", "make", ["-j3", "-s"]); } // install if ($this->args->verbose) { $this->exec("install", "make", ["install"], true); } else { $this->exec("install", "make", ["install", "-s"], true); } // activate $this->activate(); // cleanup $this->cleanup($temp); } /** * Perform any cleanups */ private function cleanup($temp = null) { if (!isset($temp)) { $temp = $this->tmp; } if (is_dir($temp)) { chdir($this->cwd); $this->info("Cleaning up %s ...\n", $temp); $this->rm($temp); } } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param string $output * @return int */ private function sudo($command, &$output) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { return -1; } $stdout = $pipes[1]; $passwd = 0; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd++ < 10) { if (stristr($data, "password")) { printf("\n%s", $data); } } $output .= $data; } return proc_close($proc); } /** * Execute a system command * @param string $name pretty name * @param string $command command * @param array $args command arguments * @param bool $sudo whether the command may need escalated privileges */ private function exec($name, $command, array $args = null, $sudo = false) { $exec = escapeshellcmd($command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->args->verbose) { $this->info("Running %s ...\n", $exec); } else { $this->info("Running %s ... ", $name); } if ($sudo && isset($this->args->sudo)) { $retval = $this->sudo(sprintf($this->args->sudo." 2>&1", $exec), $output); } elseif ($this->args->verbose) { passthru($exec ." 2>&1", $retval); } else { exec($exec ." 2>&1", $output, $retval); $output = implode("\n", $output); } if ($retval) { $this->error("Command %s failed with (%s)\n", $command, $retval); if (isset($output) && !$this->args->quiet) { printf("%s\n", $output); } exit(2); } if (!$this->args->verbose) { // we already have a bunch of output $this->info("OK\n"); } } /** * Construct a command from prefix common-name and suffix * @param type $suffix * @return string */ private function php($suffix) { $cmd = $this->args["common-name"] . $suffix; if (isset($this->args->prefix)) { $cmd = $this->args->prefix . "/bin/" . $cmd; } return $cmd; } /** * Activate extension in php.ini */ private function activate() { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $extension = basename(current(glob("modules/*.so"))); $pattern = preg_quote($extension); foreach ($files as $index => $file) { $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*extension\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { // already there $this->info("Extension already activated\n"); return; } fwrite($temp->getStream(), $line); } } // not found, add extension line to the last process file if (isset($temp, $file)) { fprintf($temp->getStream(), "extension=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $this->exec("INI owner transfer", "chown", [$ugid, $path], true); $perm = decoct($stat["mode"] & 0777); $this->exec("INI permission transfer", "chmod", [$perm, $path], true); $this->exec("INI activation", "mv", [$path, $file], true); } } } <?php namespace pharext\Openssl; class PrivateKey { /** * OpenSSL pkey resource * @var resource */ private $key; /** * Read a private key * @param string $file * @param string $password * @throws \Exception */ function __construct($file, $password) { $this->key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($this->key)) { throw new \Exception("Could not load private key"); } } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", openssl_pkey_get_details($this->key)["key"]) || !rename("$file.tmp", $file) ) { throw new \Exception(error_get_last()["message"]); } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Dump signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } if ($this->args["signature"]) { exit($this->signature($prog)); } try { if ($this->args["source"]) { if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($this, $this->args["source"]); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($this, $this->args["source"]); } else { $this->source = new SourceDir\Pharext($this, $this->args["source"]); } } } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(1); } $this->createPackage(); } function signature($prog) { try { $sig = (new Phar(Phar::running(false)))->getSignature(); printf("%s signature of %s\n%s", $sig["hash_type"], $prog, chunk_split($sig["hash"], 64, "\n")); return 0; } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); return 2; } } /** * Traverses all pharext source files to bundle * @return Generator */ private function bundle() { $rdi = new \RecursiveDirectoryIterator(__DIR__); $rii = new \RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { yield "pharext/". $rii->getSubPathname() => $rii->key(); } } private function askpass($prompt = "Password:") { system("stty -echo", $retval); if ($retval) { $this->error("Could not disable echo on the terminal\n"); } printf("%s ", $prompt); $pass = fgets(STDIN, 1024); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } /** * Creates the extension phar */ private function createPackage() { $pkguniq = uniqid(); $pkgtemp = $this->tempname($pkguniq, "phar"); $pkgdesc = "{$this->args->name}-{$this->args->release}"; $this->info("Creating phar %s ...%s", $pkgtemp, $this->args->verbose ? "\n" : " "); try { $package = new Phar($pkgtemp); if ($this->args->sign) { $this->info("\nUsing private key to sign phar ... \n"); $privkey = new Openssl\PrivateKey(realpath($this->args->sign), $this->askpass()); $privkey->sign($package); } $package->startBuffering(); $package->buildFromIterator($this->source, $this->source->getBaseDir()); $package->buildFromIterator($this->bundle(__DIR__)); $package->addFile(__DIR__."/../pharext_installer.php", "pharext_installer.php"); $package->setDefaultStub("pharext_installer.php"); $package->setStub("#!/usr/bin/php -dphar.readonly=1\n".$package->getStub()); $package->stopBuffering(); if (!chmod($pkgtemp, 0777)) { $this->error(null); } elseif ($this->args->verbose) { $this->info("Created executable phar %s\n", $pkgtemp); } else { $this->info("OK\n"); } if ($this->args->gzip) { $this->info("Compressing with gzip ... "); try { $package->compress(Phar::GZ) ->setDefaultStub("pharext_installer.php"); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } if ($this->args->bzip) { $this->info("Compressing with bzip ... "); try { $package->compress(Phar::BZ2) ->setDefaultStub("pharext_installer.php"); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); } } unset($package); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(4); } foreach (glob($pkgtemp."*") as $pkgtemp) { $pkgfile = str_replace($pkguniq, "{$pkgdesc}.ext", $pkgtemp); $pkgname = $this->args->dest ."/". basename($pkgfile); $this->info("Finalizing %s ... ", $pkgname); if (!rename($pkgtemp, $pkgname)) { $this->error(null); exit(5); } $this->info("OK\n"); if ($this->args->sign && isset($privkey)) { $keyname = $this->args->dest ."/". basename($pkgfile) . ".pubkey"; $this->info("Public Key %s ... ", $keyname); try { $privkey->exportPublicKey($keyname); $this->info("OK\n"); } catch (\Exception $e) { $this->error("%s", $e->getMessage()); } } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Command */ private $cmd; /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", $file); } /* there may be symlinks, so no realpath here */ if (!file_exists("$path/$file")) { $this->cmd->error("File %s does not exist in %s\n", $file, $path); } yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The Packager command * @var pharext\Packager */ private $cmd; /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { if (!realpath("$path/package.xml")) { throw new \Exception("Missing package.xml in $path"); } $sxe = simplexml_load_file("$path/package.xml"); $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $args = $cmd->getArgs(); if (!isset($args->name)) { $name = (string) $sxe->xpath("/pecl:package/pecl:name")[0]; foreach ($args->parse(2, ["--name", $name]) as $error) { $cmd->error("%s\n", $error); } } if (!isset($args->release)) { $release = (string) $sxe->xpath("/pecl:package/pecl:version/pecl:release")[0]; foreach ($args->parse(2, ["--release", $release]) as $error) { $cmd->error("%s\n", $error); } } $this->cmd = $cmd; $this->sxe = $sxe; $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Render installer hook * @param array $configure * @return string */ private static function loadHook($configure, $dependencies) { return include __DIR__."/../../pharext_install.tpl.php"; } /** * Create installer hook * @return \Generator */ private function generateHooks() { $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield realpath($this->path."/".end($glob)); } else { unset($dependencies[$key]); } } $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); if ($configure) { $fd = tmpfile(); ob_start(function($s) use($fd){ fwrite($fd, $s); return null; }); self::loadHook($configure, $dependencies); ob_end_flush(); rewind($fd); yield "pharext_install.php" => $fd; } } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { foreach ($this->generateHooks() as $file => $hook) { if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", is_string($hook) ? $hook : $file); } yield $file => $hook; } foreach ($this->sxe->xpath("//pecl:file") as $file) { $path = $this->path ."/". $this->dirOf($file) ."/". $file["name"]; if ($this->cmd->getArgs()->verbose) { $this->cmd->info("Packaging %s\n", $path); } if (!($realpath = realpath($path))) { $this->cmd->error("File %s does not exist", $path); } yield $realpath; } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\SourceDir; /** * A source directory containing pharext_package.php and eventually pharext_install.php */ class Pharext implements \IteratorAggregate, SourceDir { /** * @var pharext\Command */ private $cmd; /** * @var string */ private $path; /** * @var callable */ private $iter; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct(Command $cmd, $path) { $this->cmd = $cmd; $this->path = $path; $callable = include "$path/pharext_package.php"; if (!is_callable($callable)) { throw new \Exception("Package hook did not return a callable"); } $this->iter = $callable($cmd, $path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { if (!is_callable($this->iter)) { return new Git($this->cmd, $this->path); } return call_user_func($this->iter, $this->cmd, $this->path); } } <?php namespace pharext; /** * Source directory interface */ interface SourceDir extends \Traversable { /** * Read the source directory * * Note: Best practices are for others, but if you want to follow them, do * not put constructors in interfaces. Keep your complaints, I warned you. * * @param Command $cmd * @param string $path */ public function __construct(Command $cmd, $path); /** * Retrieve the base directory * @return string */ public function getBaseDir(); } <?php namespace pharext; class Tempfile extends \SplFileInfo { private $handle; function __construct($prefix) { $tries = 0; $template = sys_get_temp_dir()."/$prefix."; $omask = umask(077); do { $path = $template.uniqid(); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new \Exception("Could not create temporary file"); } parent::__construct($path); } function __destruct() { @unlink($this->getPathname()); } function closeStream() { fclose($this->handle); } function getStream() { return $this->handle; } } <?php namespace pharext; const VERSION = "2.0.0"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); À\%L’P‹‚3Á TŠg€­ô���GBMB<?xml version="1.0" encoding="ISO-8859-1" ?> <package version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>pecl_http</name> <channel>pecl.php.net</channel> <summary>Extended HTTP Support</summary> <description><![CDATA[ This HTTP extension aims to provide a convenient and powerful set of functionality for one of PHPs major applications. It eases handling of HTTP urls, headers and messages, provides means for negotiation of a client's preferred content type, language and charset, as well as a convenient way to send any arbitrary data with caching and resuming capabilities. It provides powerful request functionality with support for parallel requests. Documentation: http://devel-m6w6.rhcloud.com/mdref/http Code Coverage: http://dev.iworks.at/ext-http/lcov/ext/http/ ]]></description> <lead> <name>Michael Wallner</name> <user>mike</user> <email>mike@php.net</email> <active>yes</active> </lead> <date>2015-04-08</date> <version> <release>2.4.4dev</release> <api>2.4.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <license>BSD, revised</license> <notes><![CDATA[ * ]]></notes> <contents> <dir name="/"> <file role="doc" name="LICENSE"/> <file role="doc" name="CREDITS"/> <file role="doc" name="Exceptions.txt"/> <file role="doc" name="KnownIssues.txt"/> <file role="doc" name="ThanksTo.txt"/> <file role="src" name="config.m4"/> <file role="src" name="config9.m4"/> <file role="src" name="config.w32"/> <file role="src" name="php_http_api.h"/> <file role="src" name="php_http_buffer.c"/> <file role="src" name="php_http_buffer.h"/> <file role="src" name="php_http.c"/> <file role="src" name="php_http_client.c"/> <file role="src" name="php_http_client.h"/> <file role="src" name="php_http_client_curl.c"/> <file role="src" name="php_http_client_curl.h"/> <file role="src" name="php_http_client_request.c"/> <file role="src" name="php_http_client_request.h"/> <file role="src" name="php_http_client_response.c"/> <file role="src" name="php_http_client_response.h"/> <file role="src" name="php_http_cookie.c"/> <file role="src" name="php_http_cookie.h"/> <file role="src" name="php_http_curl.c"/> <file role="src" name="php_http_curl.h"/> <file role="src" name="php_http_encoding.c"/> <file role="src" name="php_http_encoding.h"/> <file role="src" name="php_http_env.c"/> <file role="src" name="php_http_env.h"/> <file role="src" name="php_http_env_request.c"/> <file role="src" name="php_http_env_request.h"/> <file role="src" name="php_http_env_response.c"/> <file role="src" name="php_http_env_response.h"/> <file role="src" name="php_http_etag.c"/> <file role="src" name="php_http_etag.h"/> <file role="src" name="php_http_exception.c"/> <file role="src" name="php_http_exception.h"/> <file role="src" name="php_http_filter.c"/> <file role="src" name="php_http_filter.h"/> <file role="src" name="php_http.h"/> <file role="src" name="php_http_header_parser.c"/> <file role="src" name="php_http_header_parser.h"/> <file role="src" name="php_http_header.c"/> <file role="src" name="php_http_header.h"/> <file role="src" name="php_http_info.c"/> <file role="src" name="php_http_info.h"/> <file role="src" name="php_http_message_body.c"/> <file role="src" name="php_http_message_body.h"/> <file role="src" name="php_http_message.c"/> <file role="src" name="php_http_message.h"/> <file role="src" name="php_http_message_parser.c"/> <file role="src" name="php_http_message_parser.h"/> <file role="src" name="php_http_misc.c"/> <file role="src" name="php_http_misc.h"/> <file role="src" name="php_http_negotiate.c"/> <file role="src" name="php_http_negotiate.h"/> <file role="src" name="php_http_object.c"/> <file role="src" name="php_http_object.h"/> <file role="src" name="php_http_options.c"/> <file role="src" name="php_http_options.h"/> <file role="src" name="php_http_params.c"/> <file role="src" name="php_http_params.h"/> <file role="src" name="php_http_querystring.c"/> <file role="src" name="php_http_querystring.h"/> <file role="src" name="php_http_response_codes.h"/> <file role="src" name="php_http_url.c"/> <file role="src" name="php_http_url.h"/> <file role="src" name="php_http_utf8.h"/> <file role="src" name="php_http_version.c"/> <file role="src" name="php_http_version.h"/> <dir name="tests"> <file role="test" name="skipif.inc"/> <dir name="data"> <file role="test" name="message_r_content_range.txt"/> <file role="test" name="message_r_multipart_put.txt"/> <file role="test" name="message_rr_empty.txt"/> <file role="test" name="message_rr_empty_chunked.txt"/> <file role="test" name="message_rr_empty_gzip.txt"/> <file role="test" name="message_rr_helloworld_chunked.txt"/> <file role="test" name="urls.txt"/> </dir> <dir name="helper"> <file role="test" name="cookie.inc"/> <file role="test" name="http2.crt"/> <file role="test" name="http2.key"/> <file role="test" name="pipeline.inc"/> <file role="test" name="proxy.inc"/> <file role="test" name="server.inc"/> <file role="test" name="upload.inc"/> <dir name="html"> <file role="test" name="index.html"/> </dir> </dir> <file role="test" name="bug61444.phpt"/> <file role="test" name="bug66388.phpt"/> <file role="test" name="bug66891.phpt"/> <file role="test" name="bug67932.phpt"/> <file role="test" name="bug69000.phpt"/> <file role="test" name="bug69076.phpt"/> <file role="test" name="bug69313.phpt"/> <file role="test" name="bug69357.phpt"/> <file role="test" name="client001.phpt"/> <file role="test" name="client002.phpt"/> <file role="test" name="client003.phpt"/> <file role="test" name="client004.phpt"/> <file role="test" name="client005.phpt"/> <file role="test" name="client006.phpt"/> <file role="test" name="client007.phpt"/> <file role="test" name="client008.phpt"/> <file role="test" name="client009.phpt"/> <file role="test" name="client010.phpt"/> <file role="test" name="client011.phpt"/> <file role="test" name="client012.phpt"/> <file role="test" name="client013.phpt"/> <file role="test" name="client014.phpt"/> <file role="test" name="client015.phpt"/> <file role="test" name="client016.phpt"/> <file role="test" name="client017.phpt"/> <file role="test" name="client018.phpt"/> <file role="test" name="client019.phpt"/> <file role="test" name="client020.phpt"/> <file role="test" name="client021.phpt"/> <file role="test" name="client022.phpt"/> <file role="test" name="client023.phpt"/> <file role="test" name="client024.phpt"/> <file role="test" name="client025.phpt"/> <file role="test" name="client026.phpt"/> <file role="test" name="clientrequest001.phpt"/> <file role="test" name="clientrequest002.phpt"/> <file role="test" name="clientrequest003.phpt"/> <file role="test" name="clientrequest004.phpt"/> <file role="test" name="clientresponse001.phpt"/> <file role="test" name="clientresponse002.phpt"/> <file role="test" name="clientresponse003.phpt"/> <file role="test" name="cookie001.phpt"/> <file role="test" name="cookie002.phpt"/> <file role="test" name="cookie003.phpt"/> <file role="test" name="cookie004.phpt"/> <file role="test" name="cookie005.phpt"/> <file role="test" name="cookie006.phpt"/> <file role="test" name="cookie007.phpt"/> <file role="test" name="cookie008.phpt"/> <file role="test" name="cookie009.phpt"/> <file role="test" name="cookie010.phpt"/> <file role="test" name="cookie011.phpt"/> <file role="test" name="cookie012.phpt"/> <file role="test" name="encstream001.phpt"/> <file role="test" name="encstream002.phpt"/> <file role="test" name="encstream003.phpt"/> <file role="test" name="encstream004.phpt"/> <file role="test" name="encstream005.phpt"/> <file role="test" name="encstream006.phpt"/> <file role="test" name="encstream007.phpt"/> <file role="test" name="encstream008.phpt"/> <file role="test" name="encstream009.phpt"/> <file role="test" name="envrequestbody001.phpt"/> <file role="test" name="envrequestcookie001.phpt"/> <file role="test" name="envrequestfiles001.phpt"/> <file role="test" name="envrequestfiles002.phpt"/> <file role="test" name="envrequestform.phpt"/> <file role="test" name="envrequestheader001.phpt"/> <file role="test" name="envrequestquery.phpt"/> <file role="test" name="envresponse001.phpt"/> <file role="test" name="envresponse002.phpt"/> <file role="test" name="envresponse003.phpt"/> <file role="test" name="envresponse004.phpt"/> <file role="test" name="envresponse005.phpt"/> <file role="test" name="envresponse006.phpt"/> <file role="test" name="envresponse007.phpt"/> <file role="test" name="envresponse008.phpt"/> <file role="test" name="envresponse009.phpt"/> <file role="test" name="envresponse010.phpt"/> <file role="test" name="envresponse011.phpt"/> <file role="test" name="envresponse012.phpt"/> <file role="test" name="envresponse013.phpt"/> <file role="test" name="envresponse014.phpt"/> <file role="test" name="envresponse015.phpt"/> <file role="test" name="envresponse016.phpt"/> <file role="test" name="envresponse017.phpt"/> <file role="test" name="envresponse018.phpt"/> <file role="test" name="envresponsebody001.phpt"/> <file role="test" name="envresponsebody002.phpt"/> <file role="test" name="envresponsecodes.phpt"/> <file role="test" name="envresponsecookie001.phpt"/> <file role="test" name="envresponseheader001.phpt"/> <file role="test" name="envresponseranges001.phpt"/> <file role="test" name="etag001.phpt"/> <file role="test" name="filterchunked.phpt"/> <file role="test" name="filterzlib.phpt"/> <file role="test" name="header001.phpt"/> <file role="test" name="header002.phpt"/> <file role="test" name="header003.phpt"/> <file role="test" name="header004.phpt"/> <file role="test" name="header005.phpt"/> <file role="test" name="header006.phpt"/> <file role="test" name="header007.phpt"/> <file role="test" name="header008.phpt"/> <file role="test" name="header009.phpt"/> <file role="test" name="headerparser001.phpt"/> <file role="test" name="headerparser002.phpt"/> <file role="test" name="headerparser003.phpt"/> <file role="test" name="info001.phpt"/> <file role="test" name="info002.phpt"/> <file role="test" name="message001.phpt"/> <file role="test" name="message002.phpt"/> <file role="test" name="message003.phpt"/> <file role="test" name="message004.phpt"/> <file role="test" name="message005.phpt"/> <file role="test" name="message006.phpt"/> <file role="test" name="message007.phpt"/> <file role="test" name="message008.phpt"/> <file role="test" name="message009.phpt"/> <file role="test" name="message010.phpt"/> <file role="test" name="message011.phpt"/> <file role="test" name="message012.phpt"/> <file role="test" name="message013.phpt"/> <file role="test" name="message014.phpt"/> <file role="test" name="message015.phpt"/> <file role="test" name="message016.phpt"/> <file role="test" name="messagebody001.phpt"/> <file role="test" name="messagebody002.phpt"/> <file role="test" name="messagebody003.phpt"/> <file role="test" name="messagebody004.phpt"/> <file role="test" name="messagebody005.phpt"/> <file role="test" name="messagebody006.phpt"/> <file role="test" name="messagebody007.phpt"/> <file role="test" name="messagebody008.phpt"/> <file role="test" name="messagebody009.phpt"/> <file role="test" name="messagebody010.phpt"/> <file role="test" name="messageparser001.phpt"/> <file role="test" name="messageparser002.phpt"/> <file role="test" name="negotiate001.phpt"/> <file role="test" name="params001.phpt"/> <file role="test" name="params002.phpt"/> <file role="test" name="params003.phpt"/> <file role="test" name="params004.phpt"/> <file role="test" name="params005.phpt"/> <file role="test" name="params006.phpt"/> <file role="test" name="params007.phpt"/> <file role="test" name="params008.phpt"/> <file role="test" name="params009.phpt"/> <file role="test" name="params010.phpt"/> <file role="test" name="params011.phpt"/> <file role="test" name="params012.phpt"/> <file role="test" name="params013.phpt"/> <file role="test" name="params014.phpt"/> <file role="test" name="params015.phpt"/> <file role="test" name="phpinfo.phpt"/> <file role="test" name="propertyproxy001.phpt"/> <file role="test" name="querystring001.phpt"/> <file role="test" name="querystring002.phpt"/> <file role="test" name="querystring003.phpt"/> <file role="test" name="serialize001.phpt"/> <file role="test" name="url001.phpt"/> <file role="test" name="url002.phpt"/> <file role="test" name="url003.phpt"/> <file role="test" name="url004.phpt"/> <file role="test" name="url005.phpt"/> <file role="test" name="urlparser001.phpt"/> <file role="test" name="urlparser002.phpt"/> <file role="test" name="urlparser003.phpt"/> <file role="test" name="urlparser004.phpt"/> <file role="test" name="urlparser005.phpt"/> <file role="test" name="urlparser006.phpt"/> <file role="test" name="urlparser007.phpt"/> <file role="test" name="urlparser008.phpt"/> <file role="test" name="urlparser009.phpt"/> <file role="test" name="urlparser010.phpt"/> <file role="test" name="urlparser011.phpt"/> <file role="test" name="version001.phpt"/> </dir> </dir> </contents> <dependencies> <required> <php> <min>5.3.0</min> </php> <pearinstaller> <min>1.4.1</min> </pearinstaller> <package> <name>raphf</name> <channel>pecl.php.net</channel> <min>1.0.0</min> <providesextension>raphf</providesextension> </package> <package> <name>propro</name> <channel>pecl.php.net</channel> <min>1.0.0</min> <providesextension>propro</providesextension> </package> </required> <optional> <extension><name>hash</name></extension> <extension><name>iconv</name></extension> </optional> </dependencies> <providesextension>http</providesextension> <extsrcrelease> <configureoption name="with-http-zlib-dir" prompt="where to find zlib" default="/usr" /> <configureoption name="with-http-libcurl-dir" prompt="where to find libcurl" default="/usr" /> <configureoption name="with-http-libevent-dir" prompt="where to find libevent" default="/usr" /> </extsrcrelease> <changelog /> </package> Copyright (c) 2004-2014, Michael Wallner <mike@iworks.at>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. HTTP extension for PHP Michael Wallner # Throw Exceptions: * on setters, that return self * on getters, that return objects 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. 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! dnl phpize stub of config9.m4 for pecl/http dnl $Id: config.m4 214417 2006-06-07 21:05:34Z mike $ dnl vim: noet ts=1 sw=1 sinclude(config9.m4) dnl config.m4 for pecl/http dnl $Id: config9.m4 242664 2007-09-18 19:13:37Z mike $ dnl vim: noet ts=4 sw=4 PHP_ARG_WITH([http], [whether to enable extended HTTP support], [ --with-http Enable extended HTTP support]) PHP_ARG_WITH([http-zlib-dir], [], [ --with-http-zlib-dir[=DIR] HTTP: where to find zlib], $PHP_HTTP, $PHP_HTTP) PHP_ARG_WITH([http-libcurl-dir], [], [ --with-http-libcurl-dir[=DIR] HTTP: where to find libcurl], $PHP_HTTP, $PHP_HTTP) PHP_ARG_WITH([http-libevent-dir], [], [ --with-http-libevent-dir[=DIR] HTTP: where to find libevent], $PHP_HTTP_LIBCURL_DIR, "") PHP_ARG_WITH([http-libidn-dir], [], [ --with-http-libidn-dir[=DIR] HTTP: where to find libidn], $PHP_HTTP_LIBCURL_DIR, "") if test "$PHP_HTTP" != "no"; then HTTP_HAVE_A_REQUEST_LIB=false ifdef([AC_PROG_EGREP], [ AC_PROG_EGREP ], [ AC_CHECK_PROG(EGREP, egrep, egrep) ]) ifdef([AC_PROG_SED], [ AC_PROG_SED ], [ ifdef([LT_AC_PROG_SED], [ LT_AC_PROG_SED ], [ 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]) $3 elif test "$haveext"; then AC_MSG_RESULT([yes]) AC_DEFINE([PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__), [1], [ ]) ifdef([PHP_ADD_EXTENSION_DEP], [ PHP_ADD_EXTENSION_DEP([http], $1, true) ]) $2 else AC_MSG_RESULT([no]) $3 fi ]) dnl dnl HTTP_HAVE_PHP_EXT(name[, code-if-yes[, code-if-not]]) dnl AC_DEFUN([HTTP_HAVE_PHP_EXT], [ extname=$1 haveext=$[PHP_]translit($1,a-z_-,A-Z__) AC_MSG_CHECKING([for ext/$extname support]) if test -x "$PHP_EXECUTABLE"; then grepext=`$PHP_EXECUTABLE -m | $EGREP ^$extname\$` if test "$grepext" = "$extname"; then [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 AC_MSG_RESULT([yes]) $2 else [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)= AC_MSG_RESULT([no]) $3 fi elif test "$haveext" != "no" && test "x$haveext" != "x"; then [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)=1 AC_MSG_RESULT([yes]) $2 else [PHP_HTTP_HAVE_EXT_]translit($1,a-z_-,A-Z__)= AC_MSG_RESULT([no]) $3 fi ]) dnl ---- dnl STDC dnl ---- AC_TYPE_OFF_T AC_TYPE_MBSTATE_T dnl getdomainname() is declared in netdb.h on some platforms: AIX, OSF AC_CHECK_HEADERS([netdb.h unistd.h wchar.h wctype.h arpa/inet.h]) PHP_CHECK_FUNC(gethostname, nsl) PHP_CHECK_FUNC(getdomainname, nsl) PHP_CHECK_FUNC(mbrtowc) PHP_CHECK_FUNC(mbtowc) PHP_CHECK_FUNC(iswalnum) PHP_CHECK_FUNC(inet_pton) dnl ---- dnl IDN dnl ---- AC_MSG_CHECKING([for idna.h]) IDNA_DIR= for i in "$PHP_HTTP_LIBIDN_DIR" "$IDN_DIR" /usr/local /usr /opt; do if test -f "$i/include/idna.h"; then IDNA_DIR=$i break; fi done if test "x$IDNA_DIR" != "x"; then AC_MSG_RESULT([found in $IDNA_DIR]) AC_DEFINE([PHP_HTTP_HAVE_IDN], [1], [Have libidn support]) PHP_ADD_INCLUDE($IDNA_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(idn, $IDNA_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) AC_MSG_CHECKING([for libidn version]) IDNA_VER=$(pkg-config --version libidn 2>/dev/null || echo unknown) AC_MSG_RESULT([$IDNA_VER]) AC_DEFINE_UNQUOTED([PHP_HTTP_LIBIDN_VERSION], "$IDNA_VER", [ ]) else AC_MSG_RESULT([not found]) AC_MSG_CHECKING([for idn2.h]) IDNA_DIR= for i in "$PHP_HTTP_LIBIDN_DIR" "$IDN_DIR" /usr/local /usr /opt; do if test -f "$i/include/idn2.h"; then IDNA_DIR=$i break; fi done if test "x$IDNA_DIR" != "x"; then AC_MSG_RESULT([found in $IDNA_DIR]) AC_DEFINE([PHP_HTTP_HAVE_IDN2], [1], [Have libidn2 support]) PHP_ADD_INCLUDE($IDNA_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(idn2, $IDNA_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) AC_MSG_CHECKING([for libidn2 version]) IDNA_VER=`$EGREP "define IDN2_VERSION " $IDNA_DIR/include/idn2.h | $SED -e's/^.*VERSION //g' -e 's/[[^0-9\.]]//g'` AC_MSG_RESULT([$IDNA_VER]) AC_DEFINE_UNQUOTED([PHP_HTTP_LIBIDN2_VERSION], "$IDNA_VER", [ ]) else AC_MSG_RESULT([not found]) AC_CHECK_HEADERS([unicode/uidna.h]) case $host_os in darwin*) PHP_CHECK_FUNC(uidna_IDNToASCII, icucore);; *) AC_PATH_PROG(ICU_CONFIG, icu-config, no, [$PATH:/usr/local/bin]) if test ! -x "$ICU_CONFIG"; then ICU_CONFIG="icu-config" fi AC_MSG_CHECKING([for uidna_IDNToASCII]) if ! test -x "$ICU_CONFIG"; then ICU_CONFIG=icu-config fi if $ICU_CONFIG --exists 2>/dev/null >&2; then save_LIBS=$LIBS LIBS=$($ICU_CONFIG --ldflags) AC_TRY_RUN([ #include <unicode/uidna.h> int main(int argc, char *argv[]) { return uidna_IDNToASCII(0, 0, 0, 0, 0, 0, 0); } ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_UIDNA_IDNTOASCII], [1], [ ]) LIBS=$save_LIBS PHP_EVAL_LIBLINE(`$ICU_CONFIG --ldflags`, HTTP_SHARED_LIBADD) ], [ LIBS=$save_LIBS AC_MSG_RESULT([no]) ]) fi ;; esac fi fi dnl ---- dnl ZLIB dnl ---- AC_MSG_CHECKING([for zlib.h]) ZLIB_DIR= for i in "$PHP_HTTP_ZLIB_DIR" "$PHP_ZLIB_DIR" "$PHP_ZLIB" /usr/local /usr /opt; do if test -f "$i/include/zlib.h"; then ZLIB_DIR=$i break; fi done if test "x$ZLIB_DIR" = "x"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([could not find zlib.h]) else AC_MSG_RESULT([found in $ZLIB_DIR]) AC_MSG_CHECKING([for zlib version >= 1.2.0.4]) ZLIB_VERSION=`$EGREP "define ZLIB_VERSION" $ZLIB_DIR/include/zlib.h | $SED -e 's/[[^0-9\.]]//g'` AC_MSG_RESULT([$ZLIB_VERSION]) if test `echo $ZLIB_VERSION | $SED -e 's/[[^0-9]]/ /g' | $AWK '{print $1*1000000 + $2*10000 + $3*100 + $4}'` -lt 1020004; then AC_MSG_ERROR([zlib version greater or equal to 1.2.0.4 required]) else PHP_ADD_INCLUDE($ZLIB_DIR/include) PHP_ADD_LIBRARY_WITH_PATH(z, $ZLIB_DIR/$PHP_LIBDIR, HTTP_SHARED_LIBADD) fi fi dnl ---- dnl CURL dnl ---- if test "$PHP_HTTP_LIBCURL_DIR" = "no"; then AC_DEFINE([PHP_HTTP_HAVE_CURL], [0], [ ]) else AC_MSG_CHECKING([for curl/curl.h]) CURL_DIR= for i in "$PHP_HTTP_LIBCURL_DIR" /usr/local /usr /opt; do if test -f "$i/include/curl/curl.h"; then CURL_DIR=$i break fi done if test "x$CURL_DIR" = "x"; then 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 if test -x "$i"; then CURL_CONFIG=$i break fi done if test "x$CURL_CONFIG" = "x"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([could not find curl-config]) else AC_MSG_RESULT([found: $CURL_CONFIG]) fi dnl RHEL6: 7.19.7 dnl SUSE11: 7.19.7 dnl Debian wheezy: 7.26.0 dnl Debian sqeeze: 7.21.0 dnl Debian ancient 7.18.2 AC_MSG_CHECKING([for curl version >= 7.18.2]) CURL_VERSION=`$CURL_CONFIG --version | $SED -e 's/[[^0-9\.]]//g'` AC_MSG_RESULT([$CURL_VERSION]) 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]) AC_DEFINE([PHP_HTTP_HAVE_HTTP2], [1], [ ]) else AC_MSG_RESULT([no]) fi dnl dnl compile tests dnl save_INCLUDES="$INCLUDES" INCLUDES= save_LIBS="$LIBS" LIBS=-lcurl save_CFLAGS="$CFLAGS" 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], [ ]) AC_MSG_CHECKING([for openssl support in libcurl]) AC_TRY_RUN([ #include <curl/curl.h> int main(int argc, char *argv[]) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); if (data && data->ssl_version && *data->ssl_version) { const char *ptr = data->ssl_version; while(*ptr == ' ') ++ptr; return strncasecmp(ptr, "OpenSSL", sizeof("OpenSSL")-1); } return 1; } ], [ AC_MSG_RESULT([yes]) AC_CHECK_HEADER([openssl/ssl.h], [ AC_CHECK_HEADER([openssl/crypto.h], [ AC_DEFINE([PHP_HTTP_HAVE_OPENSSL], [1], [ ]) CURL_SSL_LIBS="ssl crypto" ]) ]) ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([no]) ]) AC_MSG_CHECKING([for gnutls support in libcurl]) AC_TRY_RUN([ #include <curl/curl.h> int main(int argc, char *argv[]) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); if (data && data->ssl_version && *data->ssl_version) { const char *ptr = data->ssl_version; while(*ptr == ' ') ++ptr; return strncasecmp(ptr, "GnuTLS", sizeof("GnuTLS")-1); } return 1; } ], [ AC_MSG_RESULT([yes]) AC_CHECK_HEADER([gnutls.h], [ AC_CHECK_HEADER([gcrypt.h], [ AC_DEFINE([PHP_HTTP_HAVE_GNUTLS], [1], [ ]) CURL_SSL_LIBS="gnutls gcrypt" ]) ]) ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([no]) ]) else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING([for ares support in libcurl]) AC_TRY_RUN([ #include <curl/curl.h> int main(int argc, char *argv[]) { curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); if (data && data->ares && data->ares_num0) { return 0; } return 1; } ], [ AC_MSG_RESULT([yes]) AC_DEFINE([PHP_HTTP_HAVE_ARES], [1], [ ]) ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([no]) ]) AC_MSG_CHECKING([whether CURLOPT_TLSAUTH_TYPE expects CURL_TLSAUTH_SRP or literal "SRP"]) AC_TRY_RUN([ #include <curl/curl.h> int main(int argc, char *argv[]) { CURL *ch = curl_easy_init(); return curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, CURL_TLSAUTH_SRP); } ], [ AC_MSG_RESULT([CURL_TLSAUTH_SRP]) AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_SRP], [CURL_TLSAUTH_SRP], [ ]) AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_DEF], [CURL_TLSAUTH_NONE], [ ]) ], [ AC_TRY_RUN([ #include <curl/curl.h> int main(int argc, char *argv[]) { CURL *ch = curl_easy_init(); return curl_easy_setopt(ch, CURLOPT_TLSAUTH_TYPE, "SRP"); } ], [ AC_MSG_RESULT(["SRP"]) AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_SRP], ["SRP"], [ ]) AC_DEFINE([PHP_HTTP_CURL_TLSAUTH_DEF], [""], [ ]) ], [ 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 if test -f "$i"; then CURL_CAINFO="$i" break fi done if test "x$CURL_CAINFO" = "x"; then AC_MSG_RESULT([not found]) else 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) AC_DEFINE([PHP_HTTP_HAVE_CURL], [1], [Have libcurl support]) HTTP_HAVE_A_REQUEST_LIB=true fi fi dnl ---- dnl EVENT dnl ---- if test "$PHP_HTTP_LIBEVENT_DIR" = "no"; then AC_DEFINE([PHP_HTTP_HAVE_EVENT], [0], [ ]) else AC_MSG_CHECKING([for event2/event.h]) EVENT_DIR= for i in "$PHP_HTTP_LIBEVENT_DIR" /usr/local /usr /opt; do if test -f "$i/include/event.h"; then EVENT_DIR=$i break fi done if test "x$EVENT_DIR" = "x"; then AC_MSG_RESULT([not found]) AC_MSG_WARN([continuing without libevent support]) 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], [ ]) else AC_DEFINE([PHP_HTTP_HAVE_EVENT2], [0], [ ]) if test -f "$EVENT_DIR/include/evhttp.h" && test -f "$EVENT_DIR/include/evdns.h"; then if test -f "$EVENT_DIR/include/evrpc.h"; then EVENT_VER="1.4 or greater" else EVENT_VER="1.2 or greater" fi else EVENT_VER="1.1b or lower" fi 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]) fi fi dnl ---- dnl RAPHF dnl ---- HTTP_HAVE_PHP_EXT([raphf], [ AC_MSG_CHECKING([for php_raphf.h]) HTTP_EXT_RAPHF_INCDIR= for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../raphf; do if test -d $i; then if test -f $i/php_raphf.h; then HTTP_EXT_RAPHF_INCDIR=$i break elif test -f $i/ext/raphf/php_raphf.h; then HTTP_EXT_RAPHF_INCDIR=$i/ext/raphf break fi fi done if test "x$HTTP_EXT_RAPHF_INCDIR" = "x"; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([$HTTP_EXT_RAPHF_INCDIR]) AC_DEFINE([PHP_HTTP_HAVE_PHP_RAPHF_H], [1], [Have ext/raphf support]) PHP_ADD_INCLUDE([$HTTP_EXT_RAPHF_INCDIR]) fi ], [ AC_MSG_ERROR([Please install pecl/raphf and activate extension=raphf.$SHLIB_DL_SUFFIX_NAME in your php.ini]) ]) dnl ---- dnl PROPRO dnl ---- HTTP_HAVE_PHP_EXT([propro], [ AC_MSG_CHECKING([for php_propro.h]) HTTP_EXT_PROPRO_INCDIR= for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../propro; do if test -d $i; then if test -f $i/php_propro.h; then HTTP_EXT_PROPRO_INCDIR=$i break elif test -f $i/ext/propro/php_propro.h; then HTTP_EXT_PROPRO_INCDIR=$i/ext/propro break fi fi done if test "x$HTTP_EXT_PROPRO_INCDIR" = "x"; then AC_MSG_ERROR([not found]) else AC_MSG_RESULT([$HTTP_EXT_PROPRO_INCDIR]) AC_DEFINE([PHP_HTTP_HAVE_PHP_PROPRO_H], [1], [Have ext/propro support]) PHP_ADD_INCLUDE([$HTTP_EXT_PROPRO_INCDIR]) fi ], [ AC_MSG_ERROR([Please install pecl/propro and activate extension=propro.$SHLIB_DL_SUFFIX_NAME in your php.ini]) ]) PHP_ARG_WITH([http-shared-deps], [whether to depend on extensions which have been built shared], [ --without-http-shared-deps HTTP: do not depend on extensions like hash and iconv (when they are built shared)], $PHP_HTTP, $PHP_HTTP) dnl ---- dnl HASH dnl ---- HTTP_HAVE_PHP_EXT([hash], [ AC_MSG_CHECKING([for php_hash.h]) HTTP_EXT_HASH_INCDIR= for i in `echo $INCLUDES | $SED -e's/-I//g'` $abs_srcdir ../hash; do if test -d $i; then if test -f $i/php_hash.h; then HTTP_EXT_HASH_INCDIR=$i break elif test -f $i/ext/hash/php_hash.h; then HTTP_EXT_HASH_INCDIR=$i/ext/hash break fi fi done if test "x$HTTP_EXT_HASH_INCDIR" = "x"; then AC_MSG_RESULT([not found]) else AC_MSG_RESULT([$HTTP_EXT_HASH_INCDIR]) AC_DEFINE([PHP_HTTP_HAVE_PHP_HASH_H], [1], [Have ext/hash support]) PHP_ADD_INCLUDE([$HTTP_EXT_HASH_INCDIR]) fi ]) dnl ---- dnl ICONV dnl ---- HTTP_HAVE_PHP_EXT([iconv]) dnl ---- dnl DONE dnl ---- 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 \ " 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) AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support]) fi // 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("<not found>"); 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("<not found>"); 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"); } } /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_HTTP_API_H #define PHP_HTTP_API_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef PHP_WIN32 #include <php_config.h> #endif #include <php.h> #include <SAPI.h> #include <ext/raphf/php_raphf.h> #include <ext/propro/php_propro.h> #include <ext/standard/php_string.h> #include <ext/spl/spl_iterators.h> #include <ext/date/php_date.h> #include <zend_interfaces.h> #include <zend_exceptions.h> #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 <stddef.h> #ifdef PHP_WIN32 # define CURL_STATICLIB # include <winsock2.h> #else # ifdef HAVE_NETDB_H # include <netdb.h> # endif # ifdef HAVE_UNISTD_H # include <unistd.h> # 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 <ctype.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include <php.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <php_ini.h> #include <ext/standard/info.h> #include <zlib.h> #if PHP_HTTP_HAVE_CURL # include <curl/curl.h> # if PHP_HTTP_HAVE_EVENT # if PHP_HTTP_HAVE_EVENT2 # include <event2/event.h> # include <event2/event_struct.h> # else # include <event.h> # endif # endif #endif #if PHP_HTTP_HAVE_IDN2 # include <idn2.h> #elif PHP_HTTP_HAVE_IDN # include <idna.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include "php_http_client.h" #include <ext/spl/spl_observer.h> /* * 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_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void *)) php_persistent_handle_abandon); } 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 <event.h> # 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 <event2/event.h> # include <event2/event_struct.h> # else # error "libevent presence is unknown" # endif # endif # ifndef DBG_EVENTS # define DBG_EVENTS 0 # endif #endif #ifdef PHP_HTTP_HAVE_OPENSSL # include <openssl/ssl.h> #endif #ifdef PHP_HTTP_HAVE_GNUTLS # include <gnutls.h> #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; #endif case CURLSSLBACKEND_GSKIT: backend = "gskit"; break; 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) { 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; /* 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,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; } /* 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_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) 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 } } 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) { if (!strcasecmp("PUT", PHP_HTTP_INFO(msg).request.method)) { curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L); } else { 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 (h->rf->dtor == (void (*)(void*)) php_persistent_handle_abandon) { 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_resource_factory_init(NULL, php_persistent_handle_get_resource_factory_ops(), pf, (void (*)(void*)) php_persistent_handle_abandon); } 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 <openssl/crypto.h> # else /* !PHP_WIN32 */ # if defined(PHP_HTTP_HAVE_OPENSSL) # define PHP_HTTP_NEED_OPENSSL_TSL # include <openssl/crypto.h> # elif defined(PHP_HTTP_HAVE_GNUTLS) # define PHP_HTTP_NEED_GNUTLS_TSL # include <gcrypt.h> # 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_HTTP_CURL_H #define PHP_HTTP_CURL_H #if PHP_HTTP_HAVE_CURL #include <curl/curl.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <zlib.h> 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 <chunk size hex><chunk extension><eol> */ 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_HTTP_ENCODING_H #define PHP_HTTP_ENCODING_H #include <zlib.h> 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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, 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); \ 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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) { TSRMLS_FETCH_FROM_CTX(r->ts); long status = r->ops->get_status(r); 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); 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #ifdef PHP_HTTP_HAVE_HASH # include "php_hash.h" #endif #include <ext/standard/crc32.h> #include <ext/standard/sha1.h> #include <ext/standard/md5.h> 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <ext/spl/spl_exceptions.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 <chunk size hex><chunk extension><eol> */ 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H #define PHP_PECL_HTTP_VERSION "2.4.4dev" extern zend_module_entry http_module_entry; #define phpext_http_ptr &http_module_entry extern int http_module_number; #endif /* PHP_EXT_HTTP_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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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; 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, php_http_buffer_fix(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_buffer_fix(buffer); 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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); } } 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <ext/standard/php_lcg.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 (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 { 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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; va_list va_args; unsigned i; /* 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <ext/standard/php_lcg.h> #include <zend_exceptions.h> /* 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 prepare_escaped(zval *zv TSRMLS_DC) { if (Z_TYPE_P(zv) == IS_STRING) { 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 (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); } } 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 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) { 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_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); 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #include <php_variables.h> #include <ext/spl/spl_array.h> #ifdef PHP_HTTP_HAVE_ICONV # undef PHP_ATOM_INC # include <ext/iconv/php_iconv.h> #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #include "php_http_api.h" #if PHP_HTTP_HAVE_IDN2 # include <idn2.h> #elif PHP_HTTP_HAVE_IDN # include <idna.h> #endif #ifdef PHP_HTTP_HAVE_WCHAR # include <wchar.h> # include <wctype.h> #endif #ifdef HAVE_ARPA_INET_H # include <arpa/inet.h> #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; 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 <unicode/uidna.h> # 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 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; TSRMLS_FETCH_FROM_CTX(state->ts); #ifdef HAVE_INET_PTON if (*ptr == '[') { char *error = NULL, *tmp = memchr(ptr, ']', end - ptr); 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); efree(addr); 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 '['"; } } else { error = "expected ']'"; } if (error) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse hostinfo; %s", error); 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 */ 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 { 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; } 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; 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; 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #ifndef PHP_HTTP_URL_H #define PHP_HTTP_URL_H #include <ext/standard/url.h> /* 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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ /* +--------------------------------------------------------------------+ | 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 <mike@php.net> | +--------------------------------------------------------------------+ */ #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 */ <?php function _ext($ext) { extension_loaded($ext) or die("skip $ext extension needed\n"); } _ext("http"); function skip_online_test($message = "skip test requiring internet connection\n") { if (getenv("SKIP_ONLINE_TESTS")) { die($message); } } function skip_slow_test($message = "skip slow test\n") { if (getenv("SKIP_SLOW_TESTS")) { die($message); } } function skip_client_test($message = "skip need a client driver\n") { if (!http\Client::getAvailableDrivers()) { die($message); } } function skip_http2_test($message = "skip need http2 support (nghttpd in PATH)\n") { if (defined("http\\Client\\Curl\\HTTP_VERSION_2_0")) { foreach (explode(":", $_ENV["PATH"]) as $path) { if (is_executable($path . "/nghttpd")) { return; } } } die($message); }PUT / HTTP/1.1 User-Agent: PECL_HTTP/2.3.0dev PHP/5.6.6-dev libcurl/7.41.0-DEV Host: localhost:8000 Accept: */* Expect: 100-continue Content-Length: 3 Content-Range: bytes 1-2/3 23PUT /docs/ HTTP/1.1 User-Agent: curl/7.24.0 (x86_64-unknown-linux-gnu) libcurl/7.24.0 OpenSSL/1.0.0g zlib/1.2.6 libssh2/1.3.0 Host: drop Accept: */* Content-Length: 2273 Expect: 100-continue Content-Type: multipart/form-data; boundary=----------------------------6e182425881c ------------------------------6e182425881c Content-Disposition: form-data; name="LICENSE"; filename="LICENSE" Content-Type: application/octet-stream Copyright (c) 2011-2012, Michael Wallner <mike@iworks.at>. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------6e182425881c Content-Disposition: form-data; name="composer"; filename="composer.json" Content-Type: application/octet-stream { "name": "mike_php_net/autocracy", "type": "library", "description": "http\\Controller preserves your autocracy", "keywords": ["http", "controller", "pecl", "pecl_http"], "homepage": "http://github.com/mike-php-net/autocracy", "license": "BSD-2", "authors": [ { "name": "Michael Wallner", "email": "mike@php.net" } ], "require": { "php": ">=5.4.0", "pecl/pecl_http": "2.*" }, "autoload": { "psr-0": { "http\\Controller": "lib" } } } ------------------------------6e182425881c-- GET /default/empty.txt HTTP/1.1 Host: localhost Connection: close HTTP/1.1 200 OK Date: Wed, 25 Aug 2010 12:11:44 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT ETag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Content-Length: 0 Vary: Accept-Encoding Connection: close Content-Type: text/plain GET /default/empty.php HTTP/1.1 Connection: close Host: localhost HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 11:41:02 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 X-Powered-By: PHP/5.3.3 Vary: Accept-Encoding Connection: close Transfer-Encoding: chunked Content-Type: text/html 0 GET /default/empty.txt HTTP/1.1 Host: localhost Accept-Encoding: gzip Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 09:55:09 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT ETag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Vary: Accept-Encoding Content-Encoding: gzip Content-Length: 20 Connection: close Content-Type: text/plain ‹�0;vL���������� GET /cgi-bin/chunked.sh HTTP/1.1 Host: localhost Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 12:51:28 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Vary: Accept-Encoding Connection: close Transfer-Encoding: chunked Content-Type: text/plain 5 Hello 2 , 7 World! 0 http://www.microsoft.com http://www.opensource.org http://www.google.com http://www.yahoo.com http://www.ibm.com http://www.mysql.com http://www.oracle.com http://www.ripe.net http://www.iana.org http://www.amazon.com http://www.netcraft.com http://www.heise.de http://www.chip.de http://www.ca.com http://www.cnet.com http://www.news.com http://www.cnn.com http://www.wikipedia.org http://www.dell.com http://www.hp.com http://www.cert.org http://www.mit.edu http://www.nist.gov http://www.ebay.com http://www.postgresql.org http://www.uefa.com http://www.ieee.org http://www.apple.com http://www.sony.com http://www.symantec.com http://www.zdnet.com http://www.fujitsu.com http://www.supermicro.com http://www.hotmail.com http://www.ecma.com http://www.bbc.co.uk http://news.google.com http://www.foxnews.com http://www.msn.com http://www.wired.com http://www.sky.com http://www.usatoday.com http://www.cbs.com http://www.nbc.com http://slashdot.org http://www.bloglines.com http://www.freecode.org http://www.newslink.org http://www.un.org <?php include "server.inc"; serve(function($client) { $request = new http\Message($client, false); $cookies = new http\Cookie($request->getHeader("cookie")); $response = new http\Env\Response; $response->setCookie($cookies->setCookie("counter", $cookies->getCookie("counter")+1)); $response->send($client); }); -----BEGIN CERTIFICATE----- MIIDNzCCAh+gAwIBAgIJAKOw1awbt7aIMA0GCSqGSIb3DQEBCwUAMDIxCzAJBgNV BAYTAkFUMQ8wDQYDVQQKDAZQSFAgUUExEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0x NTAyMTIxMjQ2NTRaFw0xNzExMDcxMjQ2NTRaMDIxCzAJBgNVBAYTAkFUMQ8wDQYD VQQKDAZQSFAgUUExEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMxa+A6xEKQOYme55nQyu0qpvvGB4c4wGBNa6X6YAEzE Hc19Nbix02UZWQgHM1dmBhbVDW3stO42CQmcViIKfAy5R1A9UZjUcn9nQpaxL/sp 9LrrCANyljISXS40QcBvhCzmcUvDWBRhVTGjV+QTaYmmaM+8hJJJM6W7ByIP22Zv jHoAnzTx9sjs+OMyWmjYT9pWv6aE+u5iSC8bn3B0GgtfPwoPqDF8ePxIMBpmtbk7 JqXFzHxTVywkypnsF34XK94QjpvRcFGSg/Dc1TopJG2Wfq8hxwtoKerSlL5tyKy0 ZrltxCiLkfVZixwNZEqkg7jPSUvLS299mBrwy+ikmr8CAwEAAaNQME4wHQYDVR0O BBYEFDiHynoXXjMChfYhc1k7TNtU8ey0MB8GA1UdIwQYMBaAFDiHynoXXjMChfYh c1k7TNtU8ey0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGD9GERC uJv+oHfMwkNkDV5ZB4F+SQPzXVxDx+rJm1aGJs9PcwPNiV5GweXbvgcnvRAL4h8h uv3MLQPgVOq0iqp1QPFCoUXxbYYjYzi9FVbR/154sg0uWEHElU2e3fSjcinNRfXD 12232k6HNwSeeZUFQqn2fuk+cae5BsYT8GfsyMq5EfPtG2d8IG+Ji4eEOJeKu4gl Y33yQnHhw6QKbx8vWaDpZK8qtpapCtLrTtw/nRhX5YV6kMGK+htQUK1etV2O0VQQ OtDrhOWWaDxQULsHIvCMgTTnZqNQD+Xz4MBm3PGEGi/QUsrEaSQYppb8xxQKZU4X MyTh7rO5TdNSXmo= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAzFr4DrEQpA5iZ7nmdDK7Sqm+8YHhzjAYE1rpfpgATMQdzX01 uLHTZRlZCAczV2YGFtUNbey07jYJCZxWIgp8DLlHUD1RmNRyf2dClrEv+yn0uusI A3KWMhJdLjRBwG+ELOZxS8NYFGFVMaNX5BNpiaZoz7yEkkkzpbsHIg/bZm+MegCf NPH2yOz44zJaaNhP2la/poT67mJILxufcHQaC18/Cg+oMXx4/EgwGma1uTsmpcXM fFNXLCTKmewXfhcr3hCOm9FwUZKD8NzVOikkbZZ+ryHHC2gp6tKUvm3IrLRmuW3E KIuR9VmLHA1kSqSDuM9JS8tLb32YGvDL6KSavwIDAQABAoIBAQCzUdAB9FYJ36Vy J6qVpD69EZ7ABZzDdWhq84eY0oDQ2/ba7lhJraE2QbviU481zgzh1CponyFVNo1P paPfUxvvflWZj3Ueiq2+JjpESU81MmfR7ZOmktJBNeQWOzzHRBPT4pLgTJXprE85 s3/YX0BozWGDiIU8aIetkiR8OzXm97+BrJWiPFl733dGnHvfpHAZu/PwKZc2+8ft CnQw4GHRhTBWCVNj29HLwm+CA66zQqiAXItgijWMKzs+9ciPn+aCsCnZDNF+o1zs 4pWt60CtIJQtD3r3rSRy7nBaCKcTrr/JU3FvwqKdunuUBUsuYeSaMBokW67kpVzS dO9L9p6BAoGBAP+pvcAd8qfC1WIrn9vka3aK25ulbYSCae3YaWmABc93INJPBMvO GrcUuaLKiQC1oou+E64vGyJ9MeEFwxh2zbvU75a9ezeKAajgaq92ciMX2QqREh0N IntNOc4w7eB6BK8NpaDXNvTtxNWMvlYyhVN0M7FVQRmYJfCJdnTZAkzvAoGBAMyf 6rvWuc/wmIcAtBVe+jIeQ69EJJ/Idcjk0JUm6lFECAAraPpsCglha+wTHWWRQZ9u pPqBnb7QPjevU+3bZHnMvGoEzd5F4Rw74J+p5EZeMUJpuKmon7Ekzemcb0DV+qX9 NorB781D2Z0MG9SCptKyKpN5TPHTjGz4BB3mLC8xAoGAdq99/ynn9ClmleRameI4 YRelS2RIqzM/qcLFbMyZ5e4PtpIoT9SmYkekxgXwA/xOMUFUMZB8sE4eUbAzGbBN Yd1APGJKSUYv7w3/eOUrp07y2wzts77dOxBmvWnJhGQguINFWJ2QTbPzpI9p7OoX Kt7PAIvrZM5VDo1CCIyVnNECgYEAgLmpZXlzcwicK3GZ2EfjhVvcoIlxsMLetf6b 6PiON4lgrxqf88m7lqMezWhI+fgjHDTyvFSF89/1A/rcBaoazzSo4tka2VWEg8p3 SHoMDOh8fJcdgD2AGGRa1TeAFX2HLJzajvfp72tbnpxbdZircah7eEK60PaQRIzR qi1+ZkECgYEAi7GkO7Ey98DppLnt7567veQoEj0u8ixTlCyJ4V278nHR5+6eAZP5 PfdZVC3MtKGLnxrrPTVUy5wU0hR6Gk9EVDmrAF8TgJdnZFlBK7X23eWZ0q4qO/eO 3xIi+UrNwLag8BjYOr32nP/i+F+TLikgRIFR4oiZjk867+ofkTXmNFA= -----END RSA PRIVATE KEY----- <?php include "server.inc"; function respond($client, $msg) { $r = new http\Env\Response; $r->setEnvRequest($msg) ->setHeader("X-Req", $msg->getRequestUrl()) ->send($client); } serve(function($client) { $count = trim(fgets(STDIN)); /* the peek message */ respond($client, new http\Message($client, false)); /* pipelined messages */ $req = array(); for ($i=0; $i < $count; ++ $i) { $req[] = new http\Message($client, false); } foreach ($req as $msg) { respond($client, $msg); } }); <?php include "server.inc"; serve(function($client) { /* this might be a proxy connect or a standard request */ $request = new http\Message($client, false); if ($request->getHeader("Proxy-Connection")) { $response = new http\Env\Response; $response->setEnvRequest($request); $response->send($client); /* soak up the request following the connect */ new http\Message($client, false); } /* return the initial message as response body */ $response = new http\Env\Response; /* avoid OOM with $response->getBody()->append($request); */ $request->toStream($response->getBody()->getResource()); $response->send($client); }); <?php $php = getenv('TEST_PHP_EXECUTABLE'); if ($php) { define('PHP_BIN', $php); } else if (defined('PHP_BINARY')) { define('PHP_BIN', PHP_BINARY); } else { // PHP-5.3 define("PHP_BIN", PHP_BINDIR.DIRECTORY_SEPARATOR."php"); } function serve($cb) { /* stream_socket_server() automatically sets SO_REUSEADDR, * which is, well, bad if the tests are run in parallel */ $offset = rand(0,2000); foreach (range(8000+$offset, 9000+$offset) as $port) { if (($server = @stream_socket_server("tcp://localhost:$port"))) { fprintf(STDERR, "%s\n", $port); do { $R = array($server); $W = array(); $E = array(); $select = stream_select($R, $E, $E, 0, 10000); if ($select && ($client = stream_socket_accept($server, 1))) { if (getenv("PHP_HTTP_TEST_SSL")) { stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER); } try { while (!feof($client)) { $cb($client); } } catch (Exception $ex) { /* ignore disconnect */ if ($ex->getMessage() !== "Empty message received from stream") { fprintf(STDERR, "%s\n", $ex); } break; } } } while ($select !== false); return; } } } function server($handler, $cb) { $args = explode(' ', getenv('TEST_PHP_ARGS')); $args[] = __DIR__."/$handler"; foreach ($args as $k => $v) { if (!$v) unset($args[$k]); } proc(PHP_BIN, $args, $cb); } function nghttpd($cb) { $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w")); $offset = rand(0,2000); foreach (range(8000+$offset, 9000+$offset) as $port) { $comm = "exec nghttpd -d html $port http2.key http2.crt"; if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) { $stdin = $pipes[0]; $stdout = $pipes[1]; $stderr = $pipes[2]; usleep(50000); $status = proc_get_status($proc); if (!$status["running"]) { continue; } try { $cb($port, $stdin, $stdout, $stderr); } catch (Exception $e) { echo $e,"\n"; } proc_terminate($proc); fpassthru($stderr); fpassthru($stdout); return; } } } function proc($bin, $args, $cb) { $spec = array(array("pipe","r"), array("pipe","w"), array("pipe","w")); $comm = escapeshellcmd($bin) . " ". implode(" ", array_map("escapeshellarg", $args)); if (($proc = proc_open($comm, $spec, $pipes, __DIR__))) { $stdin = $pipes[0]; $stdout = $pipes[1]; $stderr = $pipes[2]; do { $port = trim(fgets($stderr)); $R = array($stderr); $W = array(); $E = array(); } while (is_numeric($port) && stream_select($R, $W, $E, 0, 10000)); if (is_numeric($port)) { try { $cb($port, $stdin, $stdout, $stderr); } catch (Exception $e) { echo $e,"\n"; } } proc_terminate($proc); fpassthru($stderr); fpassthru($stdout); } }<?php include "server.inc"; serve(function($client) { $request = new http\Message($client, false); if ($request->getHeader("Expect") === "100-continue") { $response = new http\Env\Response; $response->setEnvRequest($request); $response->setResponseCode(100); $response->send($client); } /* return the initial message as response body */ $response = new http\Env\Response; /* avoid OOM with $response->getBody()->append($request); */ $request->toStream($response->getBody()->getResource()); $response->send($client); }); <!doctype html> <html> <head> <meta charset="utf-8"> <title>HTTP2 Nothing to see here. --TEST-- . become _ in query strings due to php_default_treat_data() --SKIPIF-- --FILE-- 'utm_source=changed'), http\Url::JOIN_QUERY), PHP_EOL, PHP_EOL; // Replacing the host echo new http\Url($url, array('host' => 'www.google.com')), PHP_EOL, PHP_EOL; // Generating a query string from scratch echo new http\QueryString(array( 'bar.baz' => 'blah', 'utm_source' => 'google', 'utm_campaign' => 'somethingelse', 'blat' => null, )), PHP_EOL, PHP_EOL; ?> DONE --EXPECT-- http://www.example.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat http://www.example.com/foobar?bar.baz=blah&utm_source=changed&utm_campaign=somethingelse http://www.google.com/foobar?bar.baz=blah&utm_source=google&utm_campaign=somethingelse&blat bar.baz=blah&utm_source=google&utm_campaign=somethingelse DONE --TEST-- Bug #66388 (Crash on POST with Content-Length:0 and untouched body) --SKIPIF-- --FILE-- 0 ) ); $client->enqueue($request); echo $client->send()->getResponse()->getResponseCode(); ?> ===DONE=== --EXPECTF-- Test 40%d ===DONE=== --TEST-- Bug #66891 (Unexpected HTTP 401 after NTLM authentication) --SKIPIF-- --GET-- dummy=1 --FILE-- setResponseCode(200); $r->send(); var_dump(http\Env::getResponseCode()); ?> --EXPECT-- int(200)--TEST-- Bug #67932 (php://input always empty) --SKIPIF-- --PUT-- Content-Type: text/xml test --FILE-- --EXPECT-- test--TEST-- Bug #69000 (http\Url breaks down with very long URL query strings) --SKIPIF-- --FILE-- ===DONE=== --EXPECT-- Test http://foo.bar/?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ===DONE=== --TEST-- Bug #69076 (URL parsing throws exception on empty query string) --SKIPIF-- --FILE-- ===DONE=== --EXPECT-- Test http://foo.bar/ ===DONE=== --TEST-- Bug #69313 (http\Client doesn't send GET body) --SKIPIF-- --FILE-- setHeader("Content-Type", "text/plain"); $request->getBody()->append("foo"); $client = new http\Client(); $client->enqueue($request); $client->send(); echo $client->getResponse(); }); ?> Done --EXPECTF-- Test HTTP/1.1 200 OK Accept-Ranges: bytes Etag: "%s" X-Original-Transfer-Encoding: chunked Content-Length: %d GET / HTTP/1.1 User-Agent: %s Host: localhost:%d Accept: */* Content-Type: text/plain Content-Length: 3 X-Original-Content-Length: 3 foo Done --TEST-- Bug #69357 (HTTP/1.1 100 Continue overriding subsequent 200 response code with PUT request) --SKIPIF-- --FILE-- append("foo") ); $c = new \http\Client; $c->setOptions(["expect_100_timeout" => 0]); $c->enqueue($r)->send(); var_dump($c->getResponse($r)->getInfo()); var_dump($c->getResponse($r)->getHeaders()); }); ?> ===DONE=== --EXPECTF-- Test string(15) "HTTP/1.1 200 OK" array(4) { ["Accept-Ranges"]=> string(5) "bytes" ["Etag"]=> string(10) ""%x"" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(%d) } ===DONE=== --TEST-- client drivers --SKIPIF-- --FILE-- Done --EXPECTREGEX-- Test (?:bool\(true\) )+Done --TEST-- client observer --SKIPIF-- --FILE-- getProgressInfo($request) != $progress) { var_dump($progress); } } } server("proxy.inc", function($port, $stdin, $stdout, $stderr) { foreach (http\Client::getAvailableDrivers() as $driver) { $client = new http\Client($driver); $client->attach(new Observer); $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/")); $client->send(); } }); ?> Done --EXPECTREGEX-- Test P+ Done --TEST-- client once & wait --SKIPIF-- --FILE-- enqueue($request); while ($client->once()) { $client->wait(.1); } if (!$client->getResponse()) { var_dump($client); } } }); ?> Done --EXPECT-- Test Done --TEST-- client reset --SKIPIF-- --FILE-- enqueue($request)->send(); if (!($client->getResponse($request) instanceof http\Client\Response)) { var_dump($client); } try { $client->enqueue($request); } catch (Exception $e) { echo $e->getMessage(),"\n"; } $client->reset(); if (($response = $client->getResponse())) { var_dump($response); } $client->enqueue($request); } }); ?> Done --EXPECTREGEX-- Test (?:Failed to enqueue request; request already in queue )+Done --TEST-- client response callback --SKIPIF-- --FILE-- enqueue(new http\Client\Request("GET", "http://localhost:$port"), function($response) { echo "R\n"; if (!($response instanceof http\Client\Response)) { var_dump($response); } }); $client->send(); } }); ?> Done --EXPECTREGEX-- Test (?:R )+Done --TEST-- client response callback + dequeue --SKIPIF-- --FILE-- enqueue($request, "response"); $client->send(); try { $client->dequeue($request); } catch (Exception $e) { echo $e->getMessage(),"\n"; } } } }); ?> Done --EXPECTREGEX-- Test (?:(?:R Failed to dequeue request; request not in queue )+)+Done --TEST-- client response callback + requeue --SKIPIF-- --FILE-- requeue($request, "response"); $client->send(); } } }); ?> Done --EXPECTREGEX-- Test (?:R R )+Done --TEST-- client features --SKIPIF-- --FILE-- configure(array("pipelining" => true, "use_eventloop" => true)); $client->enqueue($request); $client->send(); $client->enqueue(clone $request); $client->enqueue(clone $request); $client->send(); while ($client->getResponse()) { echo "R\n"; } }); ?> Done --EXPECTREGEX-- Test (?:R R R )+Done --TEST-- client static cookies --SKIPIF-- --FILE-- setCookies(array("test" => "bar")); $client->addCookies(array("foo" => "test")); $client->enqueue($request); $client->send(); var_dump($client->getResponse()->getBody()->toString()); $request->setOptions(array("cookies" => x($client->getCookies()))); $client->requeue($request); $client->send(); var_dump($client->getResponse()->getBody()->toString()); } ?> Done --EXPECTREGEX-- Test (?:string\(46\) "Array\n\(\n \[test\] \=\> bar\n \[foo\] \=\> test\n\)\n"\nstring\(46\) "Array\n\(\n \[test\] \=\> test\n \[foo\] \=\> bar\n\)\n"\n)+Done --TEST-- client upload --SKIPIF-- --FILE-- Array \( \[name\] \=\> client010\.php \[type\] \=\> text\/plain \[tmp_name\] \=\> .+ \[error\] \=\> 0 \[size\] \=\> \d+ \) \) )+/'; $request = new http\Client\Request("POST", "http://dev.iworks.at/ext-http/.print_request.php"); $request->getBody()->addForm(null, array("file"=>__FILE__, "name"=>"upload", "type"=>"text/plain")); foreach (http\Client::getAvailableDrivers() as $driver) { $client = new http\Client($driver); $client->enqueue($request)->send(); if (!preg_match($RE, $s = $client->getResponse()->getBody()->toString())) { echo($s); } } ?> Done --EXPECT-- Test Done--TEST-- client history --SKIPIF-- --FILE-- append("foobar"); $request = new http\Client\Request; $request->setBody($body); $request->setRequestMethod("POST"); $request->setRequestUrl("http://dev.iworks.at/ext-http/.print_request.php"); $client = new http\Client; $client->recordHistory = true; $client->enqueue($request)->send(); echo $client->getHistory()->toString(true); $client->requeue($request)->send(); echo $client->getHistory()->toString(true); ?> Done --EXPECTF-- Test POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1 Content-Length: 6 foobar HTTP/1.1 200 OK Vary: %s Content-Type: text/html Date: %s Server: %s X-Original-Transfer-Encoding: chunked Content-Length: 19 string(6) "foobar" POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1 Content-Length: 6 foobar HTTP/1.1 200 OK Vary: %s Content-Type: text/html Date: %s Server: %s X-Original-Transfer-Encoding: chunked Content-Length: 19 string(6) "foobar" POST http://dev.iworks.at/ext-http/.print_request.php HTTP/1.1 Content-Length: 6 foobar HTTP/1.1 200 OK Vary: %s Content-Type: text/html Date: %s Server: %s X-Original-Transfer-Encoding: chunked Content-Length: 19 string(6) "foobar" Done --TEST-- client ssl --SKIPIF-- --FILE-- setSslOptions(array("verifypeer" => true)); $client->addSslOptions(array("verifyhost" => 2)); var_dump( array( "verifypeer" => true, "verifyhost" => 2, ) === $client->getSslOptions() ); $client->enqueue($req = new http\Client\Request("GET", "https://twitter.com/")); $client->send(); $ti = (array) $client->getTransferInfo($req); var_dump(array_key_exists("ssl_engines", $ti)); var_dump(0 < count($ti["ssl_engines"] || $ti["tls_session"]["backend"] != "openssl")); ?> Done --EXPECTF-- Test bool(true) bool(true) bool(true) Done --TEST-- client observers --SKIPIF-- --FILE-- getProgressInfo($r)) $c->pi .= "-"; } } class ProgressObserver2 implements SplObserver { function update(SplSubject $c, $r = null) { if ($c->getProgressInfo($r)) $c->pi .= "."; } } class CallbackObserver implements SplObserver { public $callback; function __construct($callback) { $this->callback = $callback; } function update(SplSubject $c, $r = null) { call_user_func($this->callback, $c, $r); } } server("proxy.inc", function($port) { $client = new Client; $client->attach($o1 = new ProgressObserver1); $client->attach($o2 = new ProgressObserver2); $client->attach( $o3 = new CallbackObserver( function ($c, $r) { $p = (array) $c->getProgressInfo($r); var_dump(array_key_exists("started", $p)); var_dump(array_key_exists("finished", $p)); var_dump(array_key_exists("dlnow", $p)); var_dump(array_key_exists("ulnow", $p)); var_dump(array_key_exists("dltotal", $p)); var_dump(array_key_exists("ultotal", $p)); var_dump(array_key_exists("info", $p)); } ) ); $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/"))->send(); var_dump(1 === preg_match("/(\.-)+/", $client->pi)); var_dump(3 === count($client->getObservers())); $client->detach($o1); var_dump(2 === count($client->getObservers())); $client->detach($o2); var_dump(1 === count($client->getObservers())); $client->detach($o3); var_dump(0 === count($client->getObservers())); }); ?> Done --EXPECTREGEX-- Test\n(bool\(true\)\n)+Done --TEST-- reset content length when resetting body --SKIPIF-- --FILE-- setBody(new http\Message\Body(fopen(__FILE__, "r"))); $client->enqueue($request); var_dump($request->getHeader("Content-Length")); $request->setBody(new http\Message\Body); $client->requeue($request); var_dump($request->getHeader("Content-Length")); ?> ===DONE=== --EXPECTF-- Test int(379) bool(false) ===DONE=== --TEST-- http client event base --SKIPIF-- --FILE-- configure(array("use_eventloop" => true)); $client2->configure(array("use_eventloop" => true)); $client1->enqueue(new http\Client\Request("GET", "http://localhost:$port/")); $client2->enqueue(new http\Client\Request("GET", "http://localhost:$port/")); $client1->send(); if (($r = $client1->getResponse())) { var_dump($r->getTransferInfo("response_code")); } if (($r = $client2->getResponse())) { var_dump($r->getTransferInfo("response_code")); } }); ?> DONE --EXPECT-- Test int(200) DONE --TEST-- client once & wait with events --SKIPIF-- --FILE-- configure(array("use_eventloop" => true)); $client->enqueue($request); while ($client->once()) { $client->wait(.1); } if (!$client->getResponse()) { var_dump($client); } } }); ?> Done --EXPECT-- Test Done --TEST-- client request gzip --SKIPIF-- --FILE-- setOptions(array("compress" => true)); $client->enqueue(new http\Client\Request("GET", "http://dev.iworks.at/ext-http/.print_request.php")); $client->send(); echo $client->getResponse(); ?> ===DONE=== --EXPECTF-- Test HTTP/1.1 200 OK Vary: Accept-Encoding Content-Type: text/html Date: %s Server: %s X-Original-Transfer-Encoding: chunked X-Original-Content-Encoding: gzip ===DONE===--TEST-- client pipelining --SKIPIF-- --FILE-- configure(array("pipelining" => true, "max_host_connections" => 0)); /* this is just to let curl know the server may be capable of pipelining */ $client->enqueue(new http\Client\Request("GET", "http://localhost:$port")); $client->send(); $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/1")); $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/2")); $client->enqueue(new http\Client\Request("GET", "http://localhost:$port/3")); $client->send(); while (($response = $client->getResponse())) { echo $response; } }); ?> ===DONE=== --EXPECT-- Test HTTP/1.1 200 OK X-Req: /3 Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK X-Req: /2 Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK X-Req: /1 Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK X-Req: / Etag: "" X-Original-Transfer-Encoding: chunked ===DONE=== --TEST-- client proxy - send proxy headers for a proxy request --SKIPIF-- getAvailableOptions()) or die("skip need libcurl with CUTLOPT_PROXYHEADER support\n"); ?> --FILE-- setOptions(array( "timeout" => 10, "proxytunnel" => true, "proxyheader" => array("Hello" => "there!"), "proxyhost" => "localhost", "proxyport" => $port, )); try { $c->enqueue($r)->send(); } catch (Exception $e) { echo $e; } echo $c->getResponse()->getBody(); }); ?> ===DONE=== --EXPECTF-- Test Server on port %d CONNECT www.example.com:80 HTTP/1.1 Host: www.example.com:80 User-Agent: PECL_HTTP/%s PHP/%s libcurl/%s Proxy-Connection: Keep-Alive Hello: there! ===DONE=== --TEST-- client proxy - don't send proxy headers for a standard request --SKIPIF-- --FILE-- setOptions(array( "timeout" => 3, "proxyheader" => array("Hello" => "there!"), )); try { $c->enqueue($r)->send(); } catch (Exception $e) { echo $e; } echo $c->getResponse()->getBody(); unset($r, $client); }); ?> ===DONE=== --EXPECTF-- Test Server on port %d GET / HTTP/1.1 User-Agent: PECL_HTTP/%s PHP/%s libcurl/%s Host: localhost:%d Accept: */* ===DONE=== --TEST-- client cookies --SKIPIF-- --FILE-- setOptions(array("cookiestore" => $tmpfile)); server("cookie.inc", function($port) use($request) { $request->setOptions(array("port" => $port)); $client = new http\Client; echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); }); server("cookie.inc", function($port) use($request) { $request->setOptions(array("port" => $port)); $client = new http\Client; echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); }); server("cookie.inc", function($port) use($request) { $request->setOptions(array("port" => $port, "cookiesession" => true)); $client = new http\Client; echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); }); server("cookie.inc", function($port) use($request) { $request->setOptions(array("port" => $port, "cookiesession" => false)); $client = new http\Client; echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); echo $client->requeue($request)->send()->getResponse(); }); unlink($tmpfile); ?> ===DONE=== --EXPECT-- Test HTTP/1.1 200 OK Set-Cookie: counter=1; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=2; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=3; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=4; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=5; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=6; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=1; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=1; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=1; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=2; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=3; Etag: "" X-Original-Transfer-Encoding: chunked HTTP/1.1 200 OK Set-Cookie: counter=4; Etag: "" X-Original-Transfer-Encoding: chunked ===DONE=== --TEST-- client http2 --SKIPIF-- --FILE-- setOptions([ "protocol" => http\Client\Curl\HTTP_VERSION_2_0, "ssl" => [ "cainfo" => __DIR__."/helper/http2.crt", ] ]); $client->enqueue(new http\Client\Request("GET", "https://localhost:$port")); echo $client->send()->getResponse(); }); ?> ===DONE=== --EXPECTF-- Test HTTP/2.0 200 %a HTTP2 Nothing to see here. ===DONE=== --TEST-- client available options and configuration --SKIPIF-- --FILE-- getOptions())) { var_dump($options); } $client->setOptions($avail = $client->getAvailableOptions()); $opt = $client->getOptions(); foreach ($avail as $k => $v) { if (is_array($v)) { $oo = $opt[$k]; foreach ($v as $kk => $vv) { if (isset($vv) && $oo[$kk] !== $vv) { var_dump(array($kk => array($vv, $oo[$kk]))); } } } else if (isset($v) && $opt[$k] !== $v) { var_dump(array($k => array($v, $opt[$k]))); } } var_dump($client === $client->configure($client->getAvailableConfiguration())); ?> ===DONE=== --EXPECT-- Test bool(true) ===DONE=== --TEST-- client deprecated methods --SKIPIF-- --FILE-- enableEvents(false); $client->enablePipelining(false); ?> ===DONE=== --EXPECTF-- Test Deprecated: Function http\Client::enableEvents() is deprecated in %sclient024.php on line %d Deprecated: Function http\Client::enablePipelining() is deprecated in %sclient024.php on line %d ===DONE=== --TEST-- client seek --SKIPIF-- --FILE-- setOptions(array("resume" => 1, "expect_100_timeout" => 0)); $request->getBody()->append("123"); echo $client->enqueue($request)->send()->getResponse(); }); // Content-length is 2 instead of 3 in older libcurls ?> ===DONE=== --EXPECTF-- Test HTTP/1.1 200 OK Accept-Ranges: bytes Etag: "%x" X-Original-Transfer-Encoding: chunked Content-Length: %d PUT / HTTP/1.1 Content-Range: bytes 1-2/3 User-Agent: %s Host: localhost:%d Accept: */* Content-Length: %d Expect: 100-continue X-Original-Content-Length: %d 23===DONE=== --TEST-- client stream 128M --SKIPIF-- --FILE-- setContentType("application/octet-stream"); for ($i = 0, $data = str_repeat("a",1024); $i < 128*1024; ++$i) { $request->getBody()->append($data); } $request->setOptions(array("timeout" => 10, "expect_100_timeout" => 0)); $client->enqueue($request); $client->send(); var_dump($client->getResponse()->getHeaders()); }); ?> ===DONE=== --EXPECTF-- Test array(5) { ["Accept-Ranges"]=> string(5) "bytes" ["Etag"]=> string(%d) "%s" ["Last-Modified"]=> string(%d) "%s" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(134217%d%d%d) } ===DONE=== --TEST-- client request --SKIPIF-- --FILE-- getBody()); var_dump($h === $r->getHeaders()); var_dump($u === $r->getRequestUrl()); var_dump($m === $r->getRequestMethod()); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) Done --TEST-- client request content type --SKIPIF-- --FILE-- setContentType($ct = "text/plain; charset=utf-8")); var_dump($ct === $r->getContentType()); ?> Done --EXPECT-- Test bool(true) bool(true) Done --TEST-- client request query --SKIPIF-- --FILE-- getQuery()); var_dump($r === $r->setQuery($q = "foo=bar")); var_dump($q === $r->getQuery()); var_dump($r === $r->addQuery("a[]=1&a[]=2")); var_dump("foo=bar&a%5B0%5D=1&a%5B1%5D=2" === $r->getQuery()); var_dump(null === $r->setQuery(null)->getQuery()); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) Done --TEST-- client request options --SKIPIF-- --FILE-- setOptions($o = array("redirect"=>5, "timeout"=>5))); var_dump($o === $r->getOptions()); var_dump($r === $r->setOptions(array("timeout"=>50))); $o["timeout"] = 50; var_dump($o === $r->getOptions()); var_dump($r === $r->setSslOptions($o = array("verify_peer"=>false))); var_dump($o === $r->getSslOptions()); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) Done --TEST-- client response cookie --SKIPIF-- --FILE-- enqueue($request)->send()->getResponse()->getCookies(0, array("comment")) as $cookies) { var_dump($cookies->toArray()); } } ?> Done --EXPECTREGEX-- Test (?:array\(7\) \{\n \["cookies"\]\=\>\n array\(2\) \{\n \["foo"\]\=\>\n string\(3\) "bar"\n \["bar"\]\=\>\n string\(3\) "foo"\n \}\n \["extras"\]\=\>\n array\(0\) \{\n \}\n \["flags"\]\=\>\n int\(0\)\n \["expires"\]\=\>\n int\(\-1\)\n \["max\-age"\]\=\>\n int\(\-1\)\n \["path"\]\=\>\n string\(0\) ""\n \["domain"\]\=\>\n string\(0\) ""\n\}\n)+Done --TEST-- client response cookies --SKIPIF-- --FILE-- enqueue($request)->send()->getResponse()->getCookies(0, array("comment")) as $cookies) { var_dump($cookies->toArray()); } } ?> Done --EXPECTREGEX-- Test (?:array\(7\) \{\n \["cookies"\]\=\>\n array\(1\) \{\n \["temp"\]\=\>\n string\(1\d\) "\d+\.\d+"\n \}\n \["extras"\]\=\>\n array\(0\) \{\n \}\n \["flags"\]\=\>\n int\(0\)\n \["expires"\]\=\>\n int\(\-1\)\n \["max\-age"\]\=\>\n int\(\-1\)\n \["path"\]\=\>\n string\(0\) ""\n \["domain"\]\=\>\n string\(0\) ""\n\}\narray\(7\) \{\n \["cookies"\]\=\>\n array\(1\) \{\n \["perm"\]\=\>\n string\(1\d\) "\d+\.\d+"\n \}\n \["extras"\]\=\>\n array\(0\) \{\n \}\n \["flags"\]\=\>\n int\(0\)\n \["expires"\]\=\>\n int\(\d+\)\n \["max\-age"\]\=\>\n int\(\-1\)\n \["path"\]\=\>\n string\(0\) ""\n \["domain"\]\=\>\n string\(0\) ""\n\}\n)+Done --TEST-- client response transfer info --SKIPIF-- --FILE-- enqueue($request)->send()->getResponse(); var_dump($response->getTransferInfo("response_code")); var_dump(count((array)$response->getTransferInfo())); } }); ?> Done --EXPECTREGEX-- Test (?:int\([1-5]\d\d\) int\(\d\d\) )+Done --TEST-- cookies empty state --SKIPIF-- --FILE-- array(), "extras" => array(), "flags" => 0, "expires" => -1, "path" => "", "domain" => "", "max-age" => -1, ); var_dump($a == $c->toArray()); var_dump($a == $o->toArray()); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- cookies expire as date --SKIPIF-- --INI-- date.timezone=UTC --FILE-- $d->format(DateTime::RFC1123))); var_dump($d->format("U") == $c->getExpires()); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- cookies numeric keys --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) DONE --TEST-- cookies raw --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) DONE --TEST-- cookies simple data --SKIPIF-- --FILE-- toArray()); foreach (array($orig, $copy) as $c) { var_dump($c->getCookie("key")); var_dump($c->getExpires()); var_dump($c->getMaxAge()); var_dump($c->getFlags()); var_dump($c->getPath()); var_dump($c->getDomain()); var_dump($c->getExtras()); var_dump($c->getCookies()); var_dump($c->toString()); var_dump( array ( "cookies" => array ( "key" => "value", ), "extras" => array ( ), "flags" => 0, "expires" => -1, "path" => "", "domain" => "", "max-age" => -1, ) == $c->toArray() ); } ?> DONE --EXPECT-- Test string(5) "value" int(-1) int(-1) int(0) NULL NULL array(0) { } array(1) { ["key"]=> string(5) "value" } string(11) "key=value; " bool(true) string(5) "value" int(-1) int(-1) int(0) NULL NULL array(0) { } array(1) { ["key"]=> string(5) "value" } string(11) "key=value; " bool(true) DONE --TEST-- cookies expire --SKIPIF-- --INI-- date.timezone=UTC --FILE-- getCookie("this")); var_dump($c->getExpires()); $o = clone $c; $t = time(); $o->setExpires(); var_dump(-1 === $o->getExpires()); var_dump(-1 != $c->getExpires()); $o->setExpires($t); var_dump($t === $o->getExpires()); var_dump($t != $c->getExpires()); var_dump( sprintf( "this=expires; expires=%s; ", date_create("@$t") ->setTimezone(new DateTimezone("UTC")) ->format("D, d M Y H:i:s \\G\\M\\T") ) === $o->toString() ); ?> DONE --EXPECT-- Test string(7) "expires" int(1327397732) bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- cookies max-age --SKIPIF-- --INI-- date.timezone=UTC --FILE-- getCookie("this")); var_dump($c->getMaxAge()); $o = clone $c; $t = 54321; $o->setMaxAge(); var_dump($o->getMaxAge()); var_dump(-1 != $c->getMaxAge()); $o->setMaxAge($t); var_dump($o->getMaxAge()); var_dump($t != $c->getMaxAge()); var_dump($o->toString()); ?> DONE --EXPECT-- Test string(7) "max-age" int(12345) int(-1) bool(true) int(54321) bool(true) string(29) "this=max-age; max-age=54321; " DONE --TEST-- cookies path --SKIPIF-- --FILE-- getCookie("this")); var_dump((string)$c); var_dump($c->getPath()); $o = clone $c; $p = "/up"; $o->setPath(); var_dump($o->getPath()); var_dump($c->getPath()); $o->setPath($p); var_dump($o->getPath()); var_dump($c->getPath()); var_dump($o->toString()); ?> DONE --EXPECT-- Test string(10) "has a path" string(33) "this=has%20a%20path; path=/down; " string(5) "/down" NULL string(5) "/down" string(3) "/up" string(5) "/down" string(31) "this=has%20a%20path; path=/up; " DONE --TEST-- cookies domain --SKIPIF-- --FILE-- getCookie("this")); var_dump((string)$c); var_dump($c->getDomain()); $o = clone $c; $d = "sub.example.com"; $o->setDomain(); var_dump($o->getDomain()); var_dump($c->getDomain()); $o->setDomain($d); var_dump($o->getDomain()); var_dump($c->getDomain()); var_dump($o->toString()); ?> DONE --EXPECT-- Test string(12) "has a domain" string(44) "this=has%20a%20domain; domain=.example.com; " string(12) ".example.com" NULL string(12) ".example.com" string(15) "sub.example.com" string(12) ".example.com" string(47) "this=has%20a%20domain; domain=sub.example.com; " DONE --TEST-- cookies flags --SKIPIF-- --FILE-- getFlags() & http\Cookie::SECURE)); var_dump(http\Cookie::HTTPONLY === ($c->getFlags() & http\Cookie::HTTPONLY)); $c->setFlags($c->getFlags() ^ http\Cookie::SECURE); var_dump(!($c->getFlags() & http\Cookie::SECURE)); var_dump(http\Cookie::HTTPONLY === ($c->getFlags() & http\Cookie::HTTPONLY)); $c->setFlags($c->getFlags() ^ http\Cookie::HTTPONLY); var_dump(!($c->getFlags() & http\Cookie::SECURE)); var_dump(!($c->getFlags() & http\Cookie::HTTPONLY)); var_dump("icanhas=flags; " === $c->toString()); $c->setFlags(http\Cookie::SECURE|http\Cookie::HTTPONLY); var_dump("icanhas=flags; secure; httpOnly; " === $c->toString()); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- cookies extras --SKIPIF-- --FILE-- "v1", "c2"=>"v2") === $c->getCookies()); var_dump(array("e0"=>"1", "e2"=>"2") === $c->getExtras()); $c->addExtra("e1", 1); $c->setExtra("e0"); $c->setExtra("e3", 123); var_dump("123" === $c->getExtra("e3")); $c->setExtra("e3"); var_dump(array("e2"=>"2", "e1"=>"1") === $c->getExtras()); var_dump("c1=v1; c2=v2; e2=2; e1=1; " === $c->toString()); $c->addExtras(array("e3"=>3, "e4"=>4)); var_dump(array("e2"=>"2", "e1"=>"1", "e3"=>"3", "e4"=>"4") === $c->getExtras()); var_dump("c1=v1; c2=v2; e2=2; e1=1; e3=3; e4=4; " === $c->toString()); $c->setExtras(array("e"=>"x")); var_dump(array("e"=>"x") === $c->getExtras()); var_dump("c1=v1; c2=v2; e=x; " === $c->toString()); $c->setExtras(); var_dump(array() === $c->getExtras()); var_dump("c1=v1; c2=v2; " === $c->toString()); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- cookies cookies --SKIPIF-- --FILE-- "v1", "c2"=>"v2") === $c->getExtras()); var_dump(array("e0"=>"1", "e2"=>"2") === $c->getCookies()); $c->addCookie("e1", 1); $c->setCookie("e0"); $c->setCookie("e3", 123); var_dump("123" === $c->getCookie("e3")); $c->setCookie("e3"); var_dump(array("e2"=>"2", "e1"=>"1") === $c->getCookies()); var_dump("e2=2; e1=1; c1=v1; c2=v2; " === $c->toString()); $c->addCookies(array("e3"=>3, "e4"=>4)); var_dump(array("e2"=>"2", "e1"=>"1", "e3"=>"3", "e4"=>"4") === $c->getCookies()); var_dump("e2=2; e1=1; e3=3; e4=4; c1=v1; c2=v2; " === $c->toString()); $c->setCookies(array("e"=>"x")); var_dump(array("e"=>"x") === $c->getCookies()); var_dump("e=x; c1=v1; c2=v2; " === $c->toString()); $c->setCookies(); var_dump(array() === $c->getCookies()); var_dump("c1=v1; c2=v2; " === $c->toString()); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- encoding stream chunked static --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) DONE --TEST-- encoding stream chunked not encoded --SKIPIF-- --FILE-- DONE --EXPECTF-- Test Notice: http\Encoding\Stream\Dechunk::decode(): Data does not seem to be chunked encoded in %s on line %d bool(true) DONE --TEST-- encoding stream chunked error --SKIPIF-- --FILE-- DONE --EXPECTF-- Test Warning: http\Encoding\Stream\Dechunk::decode(): Expected LF at pos 8 of 20 but got 0x74 in %s on line %d Warning: http\Encoding\Stream\Dechunk::decode(): Truncated message: chunk size 190 exceeds remaining data size 11 at pos 9 of 20 in %s on line %d string(14) "is ter than 1 " Warning: http\Encoding\Stream\Dechunk::decode(): Expected CRLF at pos 10 of 24 but got 0x74 0x74 in %s on line %d Warning: http\Encoding\Stream\Dechunk::decode(): Truncated message: chunk size 190 exceeds remaining data size 12 at pos 12 of 24 in %s on line %d string(15) "is er than 1 " Warning: http\Encoding\Stream\Dechunk::decode(): Expected chunk size at pos 6 of 27 but got trash in %s on line %d bool(false) DONE --TEST-- encoding stream chunked flush --SKIPIF-- --FILE-- $line) { $dech = clone $dech; if ($i % 2) { $data .= $dech->update(sprintf("%lx\r\n%s\r\n", strlen($line), $line)); } else { $data .= $dech->update(sprintf("%lx\r\n", strlen($line))); $data .= $dech->flush(); $data .= $dech->update($line); $data .= $dech->flush(); $data .= $dech->update("\r\n"); } $dech->flush(); $dech->done() and printf("uh-oh done() reported true!\n"); } $data .= $dech->update("0\r\n"); var_dump($dech->done()); $data .= $dech->finish(); var_dump(implode("", $file) === $data); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- encoding stream zlib static --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) bool(true) bool(true) DONE --TEST-- encoding stream zlib auto flush --SKIPIF-- --FILE-- update($defl->update($data))) { printf("uh-oh »%s« != »%s«\n", $data, $d); } } } echo $infl->update($defl->finish()); echo $infl->finish(); ?> DONE --EXPECT-- Test DONE --TEST-- encoding stream zlib without flush --SKIPIF-- --FILE-- update($line))) { foreach(str_split($temp) as $byte) { $data .= $infl->update($byte); } } } if (strlen($temp = $defl->finish())) { $data .= $infl->update($temp); } $data .= $infl->finish(); var_dump(implode("", $file) === $data); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- encoding stream zlib with explicit flush --SKIPIF-- --FILE-- flush(); if (strlen($temp = $defl->update($line))) { $data .= $infl->update($temp); $data .= $infl->flush(); } if (strlen($temp = $defl->flush())) { $data .= $infl->update($temp); $data .= $infl->flush(); } $defl->done() or printf("uh-oh stream's not done yet!\n"); } if (strlen($temp = $defl->finish())) { $data .= $infl->update($temp); } var_dump($defl->done()); $data .= $infl->finish(); var_dump($infl->done()); var_dump(implode("", $file) === $data); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) DONE --TEST-- encoding stream zlib error --SKIPIF-- --FILE-- DONE --EXPECTF-- Test Warning: http\Encoding\Stream\Inflate::decode(): Could not inflate data: data error in %s on line %d bool(false) DONE --TEST-- env request body --SKIPIF-- --PUT-- Content-Type: skip/me foo --FILE-- DONE --EXPECT-- string(3) "foo" DONE --TEST-- env request cookie --SKIPIF-- --COOKIE-- foo=bar;bar=123 --FILE-- getCookie()->toArray()); var_dump($r->getCookie("foo", "s")); var_dump($r->getCookie("bar", "i")); var_dump($r->getCookie("baz", "b", true)); ?> DONE --EXPECT-- Test array(2) { ["foo"]=> string(3) "bar" ["bar"]=> string(3) "123" } string(3) "bar" int(123) bool(true) DONE --TEST-- env request grabbing $_FILES --SKIPIF-- --POST_RAW-- Content-Type: multipart/form-data;boundary=--123 ----123 Content-Disposition: form-data;filename="foo.bar" foo bar ----123 Content-Disposition: form-data;filename="bar.foo" bar foo ----123-- --FILE-- getFiles(); foreach ($_FILES as $i => $file) { foreach (array("name","type","size","error","file") as $key) { if ($file[$key == "file" ? "tmp_name" : $key] != $f[$i][$key]) { printf("%d.%s differs: '%s' != '%s'\n", $i, $key, $f[$i][$key], $file[$key]); } } } ?> DONE --EXPECT-- TEST DONE --TEST-- env request grabbing $_FILES from array --SKIPIF-- --POST_RAW-- Content-Type: multipart/form-data;boundary=--123 ----123 Content-Disposition: form-data;filename=file1;name=file[] first ----123 Content-Disposition: form-data;filename=file2;name=file[] second ----123 Content-Disposition: form-data;filename=file3;name=file[] third ----123-- --FILE-- $data) { foreach ($data["tmp_name"] as $i => $file) { $f[$name][$i] = array( "file" => $file, "name" => $data["name"][$i], "size" => $data["size"][$i], "type" => $data["type"][$i], "error"=> $data["error"][$i] ); } } var_dump($f == $r->getFiles()); ?> DONE --EXPECT-- TEST bool(true) DONE --TEST-- env request form --SKIPIF-- --POST-- a=b&b=c&r[]=1&r[]=2 --FILE-- getForm()); printf("%s\n", $r->getForm("b", "s", null, true)); printf("%s\n", $r->getForm("x", "s", "nooo")); printf("%s\n", $r->getForm()); ?> DONE --EXPECT-- TEST a=b&b=c&r%5B0%5D=1&r%5B1%5D=2 c nooo a=b&r%5B0%5D=1&r%5B1%5D=2 DONE --TEST-- env request header --SKIPIF-- --POST-- a=b --ENV-- HTTP_HOST=foo.bar HTTP_ACCEPT=*/* --FILE-- --EXPECTF-- NULL string(%d) "foo.bar" string(%d) "application/x-www-form-urlencoded" array(4) { ["Accept"]=> string(3) "*/*" ["Content-Length"]=> string(1) "3" ["Content-Type"]=> string(33) "application/x-www-form-urlencoded" ["Host"]=> string(7) "foo.bar" } --TEST-- env request query --SKIPIF-- --GET-- a=b&b=c&r[]=1&r[]=2 --FILE-- getQuery()); printf("%s\n", $r->getQuery("b", "s", null, true)); printf("%s\n", $r->getQuery("x", "s", "nooo")); printf("%s\n", $r->getQuery()); ?> DONE --EXPECT-- TEST a=b&b=c&r%5B0%5D=1&r%5B1%5D=2 c nooo a=b&r%5B0%5D=1&r%5B1%5D=2 DONE --TEST-- env response message --SKIPIF-- --POST-- a=b --ENV-- HTTP_ACCEPT_ENCODING=gzip --FILE-- setHeader("foo","bar"); $r->setContentEncoding(http\env\Response::CONTENT_ENCODING_GZIP); $r->setBody(new http\message\Body(fopen(__FILE__,"r"))); $r->send(); --EXPECTHEADERS-- Foo: bar Content-Encoding: gzip Vary: Accept-Encoding --EXPECTREGEX-- ^\x1f\x8b\x08.+ --TEST-- env response cache negative --SKIPIF-- --GET-- a=b --ENV-- HTTP_IF_MODIFIED_SINCE=Fri, 13 Feb 2009 23:31:30 GMT HTTP_IF_NONE_MATCH=0000-00-0000 --FILE-- setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->send(); ?> --EXPECTHEADERS-- ETag: "abc" Last-Modified: Fri, 13 Feb 2009 23:31:31 GMT --EXPECT-- setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->send(); ?> --TEST-- env response ranges --SKIPIF-- --ENV-- HTTP_RANGE=bytes=2-4 --GET-- a=b --FILE-- setContentType("text/plain"); $r->setContentDisposition( array("attachment" => array(array("filename" => basename(__FILE__)))) ); $r->setBody(new http\Message\Body(fopen(__FILE__, "rb"))); $r->send(); ?> --EXPECTHEADERSF-- Content-Type: text/plain%s --EXPECTF-- php --TEST-- env response callback --SKIPIF-- --GET-- dummy=1 --FILE-- setCacheControl("public,must-revalidate,max-age=0"); $r->setThrottleRate(1, 0.1); ob_start($r); echo "foo"; echo "bar"; ob_end_flush(); $r->send(); ?> --EXPECTHEADERS-- Accept-Ranges: bytes Cache-Control: public,must-revalidate,max-age=0 ETag: "9ef61f95" --EXPECTF-- foobar --TEST-- env response cache positive --SKIPIF-- --GET-- a=b --ENV-- HTTP_IF_MODIFIED_SINCE=Fri, 13 Feb 2009 23:31:32 GMT --FILE-- setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->isCachedByEtag("If-None-Match") and die("Huh? etag? really?\n"); $r->isCachedByLastModified("If-Modified-Since") or die("yep, I should be cached"); $r->send(); ?> --EXPECTHEADERS-- HTTP/1.1 304 Not Modified ETag: "abc" Last-Modified: Fri, 13 Feb 2009 23:31:31 GMT --EXPECT-- --TEST-- env response stream --SKIPIF-- --FILE-- addHeader("foo", array("bar","baz")); $r->getBody()->append("foo"); $r->send($f); rewind($f); var_dump(stream_get_contents($f)); ?> Done --EXPECT-- Test string(115) "HTTP/1.1 200 OK Accept-Ranges: bytes Foo: bar, baz ETag: "8c736521" Transfer-Encoding: chunked 3 foo 0 " Done --TEST-- env response env request --SKIPIF-- --GET-- dummy=1 --FILE-- setHeader("Range", "bytes=2-4"); $res = new http\Env\Response; $res->setEnvRequest($req); $res->setContentType("text/plain"); $res->getBody()->append("012345679"); $res->send($tmp); rewind($tmp); var_dump(stream_get_contents($tmp)); ?> Done --EXPECTF-- Test string(%d) "HTTP/1.1 206 Partial Content%c Accept-Ranges: bytes%c X-Powered-By: %s%c Content-Type: text/plain%c Content-Range: bytes 2-4/9%c Transfer-Encoding: chunked%c %c 3%c 234%c 0%c %c " Done --TEST-- env response stream message --SKIPIF-- --ENV-- HTTP_ACCEPT_ENCODING=gzip --FILE-- setHeader("foo","bar"); $r->setContentEncoding(http\env\Response::CONTENT_ENCODING_GZIP); $r->setBody(new http\message\Body(fopen(__FILE__,"r"))); $r->send($f); rewind($f); var_dump(stream_get_contents($f)); ?> --EXPECTREGEX-- string\(\d+\) "HTTP\/1\.1 200 OK Accept-Ranges: bytes Foo: bar Content-Encoding: gzip Vary: Accept-Encoding ETag: "\w+-\w+-\w+" Last-Modified: \w+, \d+ \w+ \d{4} \d\d:\d\d:\d\d GMT Transfer-Encoding: chunked d0 \x1f\x8b\x08.+ 0 " --TEST-- env response stream cache negative --SKIPIF-- --GET-- a=b --ENV-- HTTP_IF_MODIFIED_SINCE=Fri, 13 Feb 2009 23:31:30 GMT HTTP_IF_NONE_MATCH=0000-00-0000 --FILE-- setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->send($f); rewind($f); var_dump(stream_get_contents($f)); ?> --EXPECTF-- string(%d) "HTTP/1.1 200 OK%c Accept-Ranges: bytes%c X-Powered-By: %s%c ETag: "abc"%c Last-Modified: %s%c Transfer-Encoding: chunked%c %c e1%c setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->send($f); rewind($f); var_dump(stream_get_contents($f)); ?> %c 0%c %c " --TEST-- env response stream ranges --SKIPIF-- --ENV-- HTTP_RANGE=bytes=2-4 --GET-- a=b --FILE-- setContentType("text/plain"); $r->setContentDisposition( array("attachment" => array(array("filename" => basename(__FILE__)))) ); $r->setBody(new http\Message\Body(fopen(__FILE__, "rb"))); $r->send($f); rewind($f); var_dump(stream_get_contents($f)); ?> --EXPECTF-- string(%i) "HTTP/1.1 206 Partial Content%c Accept-Ranges: bytes%c X-Powered-By: PHP/%s%c Content-Type: text/plain%c Content-Range: bytes 2-4/311%c Transfer-Encoding: chunked%c %c 3%c php%c 0%c %c " --TEST-- env response cache positive with env message --SKIPIF-- --GET-- dummy=1 --FILE-- setHeader("If-Modified-Since", "Fri, 13 Feb 2009 23:31:32 GMT"); $r = new http\Env\Response; $r->setEnvRequest($e); $r->setBody(new http\Message\Body(fopen(__FILE__,"rb"))); $r->setEtag("abc"); $r->setLastModified(1234567891); $r->isCachedByEtag("If-None-Match") and die("Huh? etag? really?\n"); $r->isCachedByLastModified("If-Modified-Since") or die("yep, I should be cached"); $r->send(); ?> --EXPECTHEADERS-- HTTP/1.1 304 Not Modified ETag: "abc" Last-Modified: Fri, 13 Feb 2009 23:31:31 GMT --EXPECT-- --TEST-- env response content disposition --SKIPIF-- --GET-- dummy=1 --FILE-- setContentDisposition(array("attachment"=>array("filename"=>basename(__FILE__)))); $r->setBody(new http\Message\Body(fopen(__FILE__,"r"))); $r->send(); ?> --EXPECTHEADERS-- Content-Disposition: attachment;filename=envresponse012.php --EXPECT-- setContentDisposition(array("attachment"=>array("filename"=>basename(__FILE__)))); $r->setBody(new http\Message\Body(fopen(__FILE__,"r"))); $r->send(); ?> --TEST-- env response deflate --SKIPIF-- --GET-- dummy=1 --FILE-- setHeader("Accept-Encoding", "deflate"); $res = new http\Env\Response; $res->setCacheControl("public, max-age=3600"); $res->setContentEncoding(http\Env\Response::CONTENT_ENCODING_GZIP); $res->getBody()->append("foobar"); $res->setEnvRequest($req); $res->send(); ?> --EXPECTHEADERS-- Content-Encoding: deflate Vary: Accept-Encoding Cache-Control: public, max-age=3600 --EXPECTREGEX-- ^\x78\x9c.+ --TEST-- env response invalid ranges --SKIPIF-- --FILE-- setHeader("Range", "bytes=321-123,123-0"); $res = new http\Env\Response; $res->getBody()->append("foobar"); $res->setEnvRequest($req); $res->send($f); rewind($f); var_dump(stream_get_contents($f)); --EXPECTF-- string(129) "HTTP/1.1 416 Requested Range Not Satisfiable Accept-Ranges: bytes Content-Range: bytes */6 Transfer-Encoding: chunked 0 " --TEST-- env response send replaced body using multiple ob_start --SKIPIF-- --FILE-- getBody(); $r->setBody(new http\Message\Body); ob_start($r); echo "foo: $b\n"; ob_end_flush(); $f = fopen("php://memory", "r+"); $r->send($f); fseek($f, 0, SEEK_SET); echo stream_get_contents($f); ?> --EXPECT-- HTTP/1.1 200 OK Accept-Ranges: bytes ETag: "fc8305a1" Transfer-Encoding: chunked 9 foo: bar 0 --TEST-- env response send failure --SKIPIF-- --FILE-- getBody()->append(str_repeat("a", 16*1024*4)); $s = fopen("php://temp", "w"); stream_filter_append($s, "closer"); var_dump($r->send($s)); ?> DONE --EXPECTF-- Test Warning: http\Env\Response::send(): Failed to send response body in %s on line %d bool(false) DONE --TEST-- env response stream: no chunked transfer encoding for CONNECTs --SKIPIF-- --FILE-- setRequestMethod("CONNECT"); $req->setRequestUrl(array("host"=>"www.example.com", "port"=>80)); echo $req; $res = new http\Env\Response; $res->setEnvRequest($req); $res->send(STDOUT); ?> ===DONE=== --EXPECTF-- Test CONNECT www.example.com:80 HTTP/1.1 HTTP/1.1 200 OK ===DONE=== --TEST-- env response don't generate stat based etag for temp stream --SKIPIF-- --FILE-- append("1234567890\n"); $r = new http\Env\Response; $r->setBody($b); $r->send(STDOUT); ?> ===DONE=== --EXPECTF-- Test HTTP/1.1 200 OK Accept-Ranges: bytes ETag: "%x" Last-Modified: %s Transfer-Encoding: chunked b 1234567890 0 ===DONE=== --TEST-- env response body --SKIPIF-- --INI-- output_buffering=1 --FILE-- getBody()); ?> Done --EXPECTF-- Test string(5) "Test " Done --TEST-- env response body error --SKIPIF-- --INI-- output_buffering=1 --FILE-- getBody()); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } ?> Done --EXPECTF-- Test http\Env\Response::__construct(): Could not fetch response body, output has already been sent at %senvresponsebody002.php:3 Done --TEST-- env response codes --SKIPIF-- --FILE-- Done --EXPECT-- Test array(0) { } Done --TEST-- env response cookie --SKIPIF-- --FILE-- addCookie("foo","bar"); $c->setMaxAge(60); $r->setCookie($c); $r->setCookie("baz"); $r->setCookie(123); $r->send(STDOUT); ?> --EXPECT-- HTTP/1.1 200 OK Set-Cookie: foo=bar; max-age=60; Set-Cookie: baz=1; Set-Cookie: 123=1; ETag: "" Transfer-Encoding: chunked 0 --TEST-- env response header --SKIPIF-- --FILE-- %s [Foo] => bar [More] => Array ( [0] => than [1] => what's [2] => good ) [Content-Type] => %s ) Created --TEST-- ranges --SKIPIF-- --GET-- a=b --ENV-- HTTP_RANGE=bytes=-3,000-001,1-1,0-0,100- --FILE-- setBody(new http\Message\Body(fopen(__FILE__, "rb"))); $r->send(); ?> --EXPECTF-- --%s%c Content-Type: application/octet-stream%c Content-Range: bytes 107-109/110%c %c ?> %c --%s%c Content-Type: application/octet-stream%c Content-Range: bytes 0-1/110%c %c %c --%s----TEST-- etags with hash --SKIPIF-- =") or die("skip PHP>=5.4 required"); ?> --FILE-- append("Hello, my old fellow."); foreach (hash_algos() as $algo) { switch ($algo) { case "gost-crypto": case "fnv1a32": case "fnv1a64": continue 2; } ini_set("http.etag.mode", $algo); printf("%10s: %s\n", $algo, $body->etag() ); } ?> DONE --EXPECT-- md2: 9bf7d8506d7453a85dc34fa730cbc16a md4: 137008b9144843f5bfcc6651688acc41 md5: 6ce3cc8f3861fb7fd0d77739f11cd29c sha1: ad84012eabe27a61762a97138d9d2623f4f1a7a9 sha224: 91be9bd30cec7fb7fb0279e40211fa71f8a7ab933f9f1a832d7c60cb sha256: ed9ecfe5c76d51179c3c1065916fdb8d94aee05577f187bd763cdc962bba1f42 sha384: 923a756152da113db192958da485c7881e7c4811d2d34e22f4d74cd45310d983f7fb1c5527a5f9037a4c7b649a6cc2b2 sha512: ad5ea693b8df4457d08d835ad5ccf7b626b66285f8424b3ec59e54c63bf63feef9a92baaba71c38d7bd9a1135488499fc835a8818390965c9ce8a5e4c40e519f ripemd128: b9e8d5864b5821d72e66101a9a0e730a ripemd160: d697a33676aece781b72f6fcb95f4c730367706b ripemd256: 9c3a73ab03e6d7d3471cf70316c4ff3ec56212d25730d382fb1480346529742b ripemd320: 5a6ee6b7c35c64d9c91019b9a1ceb2ab2ae19915f3dc96b0f244e15581d750a775a3682c5e70ee23 whirlpool: 2cb738084edaede8b36e9c8d81f5d30d9afe12bf60715073a6651c32c3448a6eeeff9f9715a8c996291ab3cd6c9a9caac8bea3b0eeb1c88afe6ad46fdd0cef83 tiger128,3: f3055bdb40b06abac716a27a654b295d tiger160,3: f3055bdb40b06abac716a27a654b295dc07e1ab9 tiger192,3: f3055bdb40b06abac716a27a654b295dc07e1ab915b56529 tiger128,4: e6a1628a4da8fa6adf4ca866c5e235b5 tiger160,4: e6a1628a4da8fa6adf4ca866c5e235b51939bb61 tiger192,4: e6a1628a4da8fa6adf4ca866c5e235b51939bb61ecf8423f snefru: 8f50c66c8f0a1510f9c591a2b7a070853d4770c60a38394c8857918dd91a2e5b snefru256: 8f50c66c8f0a1510f9c591a2b7a070853d4770c60a38394c8857918dd91a2e5b gost: efc79cdd01331adf80b30af816ff7a934f3f3df3085294a310918cacff3500f0 adler32: 4ff5075d crc32: 757b06f7 crc32b: e56655c5 fnv132: ebd1fa1f fnv164: 9790ce01eba3ae9f joaat: 70a407c9 haval128,3: 68a1bee33d2a4fa5543be7fa871f84ea haval160,3: b4204e8c4f3c993385d997539afa723888700bbd haval192,3: 6c7f3442f5b5c7d338bd31ab9de1216576ce6633f8de9e03 haval224,3: 4edf7debeea48b94af73f47c1a4449dff516b69ba36f6659ed59689c haval256,3: eb919a27c9e598cf3559e79fca10119d54b6f704b779cd665ab5352eb17726c4 haval128,4: 184195034f2e5b2a0d04dcc42fac3275 haval160,4: b13d521378d7b74b226430355fa6f4ceba0782c2 haval192,4: 4e53f767e7dbff4abb8ebf767d672db3df77de7d9de6e9d9 haval224,4: 1208cc9fc1c23de3985f5a5214ebb67c846cecd32f96d950ef3ef770 haval256,4: 658d40b21f87ebe45cf6ec822402d1ca6965f263358e3927a92beba837785735 haval128,5: 938933eefe94e217d73a27909f89f8c6 haval160,5: 07b9e4a6c451acb5930081f414a06d948c1b70ba haval192,5: 997ca1515369b0051e9fcc736c1096618ef936f185a19ebe haval224,5: b46f2aada87d9e7a38b126268dce9779303aa4999d42f5c74427e362 haval256,5: 4e0b601e5ee93d6c2a449793e756e9ca6e03fb618c9f2ed849a7f8ca29ef9112 DONE --TEST-- chunked filter --SKIPIF-- --FILE-- DONE --EXPECT-- DONE --TEST-- zlib filter --SKIPIF-- --FILE-- DONE --EXPECT-- DONE --TEST-- header string --SKIPIF-- --FILE-- Done --EXPECT-- Test bool(true) Done --TEST-- header numeric --SKIPIF-- --FILE-- Done --EXPECT-- Test bool(true) Done --TEST-- header serialize --SKIPIF-- --FILE-- Done --EXPECT-- Test bool(true) bool(true) Done --TEST-- header match --SKIPIF-- --FILE-- match("gzip", http\Header::MATCH_WORD)); var_dump($ae->match("gzip", http\Header::MATCH_WORD|http\Header::MATCH_CASE)); var_dump($ae->match("gzip", http\Header::MATCH_STRICT)); var_dump($ae->match("deflate", http\Header::MATCH_WORD)); var_dump($ae->match("deflate", http\Header::MATCH_WORD|http\Header::MATCH_CASE)); var_dump($ae->match("deflate", http\Header::MATCH_STRICT)); var_dump($ae->match("zip", http\Header::MATCH_WORD)); var_dump($ae->match("gzip", http\Header::MATCH_FULL)); ?> Done --EXPECT-- Test bool(true) bool(true) bool(false) bool(true) bool(true) bool(false) bool(false) bool(false) Done --TEST-- header negotiation --SKIPIF-- --FILE-- negotiate(array("text/plain","text/html"))); var_dump("text/html" === $a->negotiate(array("text/plain","text/html"), $rs)); var_dump(array("text/html"=>0.99, "text/plain"=>0.5) === $rs); var_dump("text/plain" === $a->negotiate(array("foo/bar", "text/plain"), $rs)); var_dump(array("text/plain"=>0.5) === $rs); var_dump("foo/bar" === $a->negotiate(array("foo/bar"), $rs)); var_dump(array() === $rs); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) Done --TEST-- header parsing --SKIPIF-- --FILE-- "bar","Bar"=>"foo") === http\Header::parse($header)); $headers = http\Header::parse($header, "http\\Header"); var_dump(2 === count($headers)); var_dump(2 === array_reduce($headers, function($count, $header) { return $count + ($header instanceof http\Header); }, 0)); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) Done --TEST-- header parse error --SKIPIF-- --FILE-- Done --EXPECTF-- Test Warning: http\Header::parse(): Failed to parse headers: unexpected end of line at pos 4 of 'wass\nup' in %s on line %d Done --TEST-- header params --SKIPIF-- --FILE-- array("value" => true, "arguments" => array()), "must-revalidate" => array("value" => true, "arguments" => array()), "max-age" => array("value" => "0", "arguments" => array()), ) === $header->getParams()->params ); ?> Done --EXPECT-- Test bool(true) Done --TEST-- header params w/ args --SKIPIF-- --FILE-- array("value" => "bar", "arguments" => array()), "bar" => array("value" => "bis", "arguments" => array("bis" => "where")) ) === $header->getParams(".", "where", "is")->params ); ?> Done --EXPECT-- Test bool(true) Done --TEST-- header parser --SKIPIF-- --FILE-- "FAILURE",0=>"START","KEY","VALUE","VALUE_EX","HEADER_DONE","DONE"); $parser = new http\Header\Parser; do { $state = $parser->parse($part = array_shift($headers), $headers ? 0 : http\Header\Parser::CLEANUP, $result); printf("%2\$-32s | %1\$s\n", $states[$state], addcslashes($part, "\r\n\t\0")); } while ($headers && $state !== http\Header\Parser::STATE_FAILURE); var_dump($result); ?> ===DONE=== --EXPECT-- Test One: | VALUE header\n | VALUE_EX Two: header\n\tlines\n | VALUE_EX Three | KEY : header\n lines\n here\n | VALUE_EX More: than one header\n | VALUE_EX More: | VALUE than: | VALUE you: | VALUE expect\n | VALUE_EX \n | DONE array(4) { ["One"]=> string(6) "header" ["Two"]=> string(12) "header lines" ["Three"]=> string(17) "header lines here" ["More"]=> array(2) { [0]=> string(15) "than one header" [1]=> string(17) "than: you: expect" } } ===DONE=== --TEST-- header parser errors --SKIPIF-- --FILE-- parse($header, http\Header\Parser::CLEANUP, $parsed), $parsed); } ?> ===DONE=== --EXPECTF-- Test Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 2 of 'Na\000me' in %sheaderparser002.php on line %d int(-1) array(0) { } Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected end of line at pos 2 of 'Na\nme: value' in %sheaderparser002.php on line %d int(-1) array(0) { } Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 0 of '\000value' in %sheaderparser002.php on line %d int(-1) array(0) { } Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected end of input at pos 5 of 'value' in %sheaderparser002.php on line %d int(-1) array(0) { } Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 3 of 'val\000ue' in %sheaderparser002.php on line %d int(-1) array(0) { } Warning: http\Header\Parser::parse(): Failed to parse headers: unexpected character '\000' at pos 5 of 'value\000' in %sheaderparser002.php on line %d int(-1) array(0) { } ===DONE===--TEST-- header parser with nonblocking stream --SKIPIF-- --FILE-- stream($socket[0], 0, $hdrs); fwrite($socket[1], $line); var_dump($parser->getState()); var_dump($parser->stream($socket[0], 0, $hdrs)); } var_dump($hdrs); ?> DONE --EXPECT-- Test int(0) int(1) int(1) int(2) int(2) int(3) int(3) int(1) int(1) int(3) int(3) int(5) array(2) { ["Host"]=> string(9) "localhost" ["Content-Length"]=> string(1) "3" } DONE --TEST-- invalid HTTP info --SKIPIF-- --FILE-- DONE --EXPECTF-- exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 3 of 'GET HTTP/1.1'' in %s Stack trace: #0 %s: http\Message->__construct('GET HTTP/1.1') #1 {main} exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 3 of 'GET HTTP/1.123'' in %s Stack trace: #0 %s: http\Message->__construct('GET HTTP/1.123') #1 {main} exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\057' at pos 7 of 'GETHTTP/1.1'' %s Stack trace: #0 %s: http\Message->__construct('GETHTTP/1.1') #1 {main} object(http\Message)#%d (9) { ["type":protected]=> int(1) ["body":protected]=> NULL ["requestMethod":protected]=> string(3) "GET" ["requestUrl":protected]=> string(1) "/" ["responseStatus":protected]=> string(0) "" ["responseCode":protected]=> int(0) ["httpVersion":protected]=> string(3) "1.1" ["headers":protected]=> array(0) { } ["parentMessage":protected]=> NULL } DONE --TEST-- invalid HTTP info --SKIPIF-- --FILE-- ===DONE=== --EXPECTF-- Test exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\057' at pos 4 of 'HTTP/1.1 99 Apples in my Basket'' in %sinfo002.php:%d Stack trace: #0 %sinfo002.php(%d): http\Message->__construct('HTTP/1.1 99 App...') #1 %sinfo002.php(%d): {closure}() #2 %sinfo002.php(%d): trap(Object(Closure)) #3 {main} exception 'http\Exception\BadMessageException' with message 'http\Message::__construct(): Failed to parse headers: unexpected character '\040' at pos 7 of 'CONNECT HTTP/1.1'' in %sinfo002.php:%d Stack trace: #0 %sinfo002.php(%d): http\Message->__construct('CONNECT HTTP/1....') #1 %sinfo002.php(%d): {closure}() #2 %sinfo002.php(%d): trap(Object(Closure)) #3 {main} HTTP/1.1 200 CONNECT www.example.org:80 HTTP/1.1 ===DONE=== --TEST-- message --SKIPIF-- --FILE-- getHttpVersion(), $m->getType()==HttpMessage::TYPE_NONE, $m->getHeaders() ); $m = new HttpMessage("GET / HTTP/1.1\r\n"); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_REQUEST, $m->getRequestMethod(), $m->getRequestUrl(), $m->getHeaders() ); $m = new HttpMessage("HTTP/1.1 200 Okidoki\r\n"); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo "---\n"; $m = new HttpMessage(file_get_contents(__DIR__."/data/message_rr_empty.txt")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(file_get_contents(__DIR__."/data/message_rr_empty_gzip.txt")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(file_get_contents(__DIR__."/data/message_rr_empty_chunked.txt")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(file_get_contents(__DIR__."/data/message_rr_helloworld_chunked.txt")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); echo "---\n"; $m = new HttpMessage(fopen(__DIR__."/data/message_rr_empty.txt", "r+b")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(fopen(__DIR__."/data/message_rr_empty_gzip.txt", "r+b")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(fopen(__DIR__."/data/message_rr_empty_chunked.txt", "r+b")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); $m = new HttpMessage(fopen(__DIR__."/data/message_rr_helloworld_chunked.txt", "r+b")); echo $m; var_dump( $m->getHttpVersion(), $m->getType()==HttpMessage::TYPE_RESPONSE, $m->getResponseCode(), $m->getResponseStatus(), $m->getHeaders() ); echo $m->getParentMessage(); echo "Done\n"; --EXPECTF-- Test string(3) "1.1" bool(true) array(0) { } GET / HTTP/1.1 string(3) "1.1" bool(true) string(3) "GET" string(1) "/" array(0) { } HTTP/1.1 200 Okidoki string(3) "1.1" bool(true) int(200) string(7) "Okidoki" array(0) { } --- HTTP/1.1 200 OK Date: Wed, 25 Aug 2010 12:11:44 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT Etag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Content-Length: 0 Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Content-Length: 0 string(3) "1.1" bool(true) int(200) string(2) "OK" array(10) { ["Date"]=> string(29) "Wed, 25 Aug 2010 12:11:44 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Last-Modified"]=> string(29) "Wed, 28 Apr 2010 10:54:37 GMT" ["Etag"]=> string(23) ""2002a-0-48549d615a35c"" ["Accept-Ranges"]=> string(5) "bytes" ["Content-Length"]=> string(1) "0" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Content-Length"]=> string(1) "0" } GET /default/empty.txt HTTP/1.1 Host: localhost Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 09:55:09 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT Etag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Content-Length: 20 X-Original-Content-Encoding: gzip string(3) "1.1" bool(true) int(200) string(2) "OK" array(10) { ["Date"]=> string(29) "Thu, 26 Aug 2010 09:55:09 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Last-Modified"]=> string(29) "Wed, 28 Apr 2010 10:54:37 GMT" ["Etag"]=> string(23) ""2002a-0-48549d615a35c"" ["Accept-Ranges"]=> string(5) "bytes" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Content-Length"]=> string(2) "20" ["X-Original-Content-Encoding"]=> string(4) "gzip" } GET /default/empty.txt HTTP/1.1 Host: localhost Accept-Encoding: gzip Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 11:41:02 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 X-Powered-By: PHP/5.3.3 Vary: Accept-Encoding Connection: close Content-Type: text/html X-Original-Transfer-Encoding: chunked Content-Length: 0 string(3) "1.1" bool(true) int(200) string(2) "OK" array(8) { ["Date"]=> string(29) "Thu, 26 Aug 2010 11:41:02 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["X-Powered-By"]=> string(9) "PHP/5.3.3" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(9) "text/html" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(0) } GET /default/empty.php HTTP/1.1 Connection: close Host: localhost HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 12:51:28 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Transfer-Encoding: chunked Content-Length: 14 Hello, World! string(3) "1.1" bool(true) int(200) string(2) "OK" array(7) { ["Date"]=> string(29) "Thu, 26 Aug 2010 12:51:28 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(14) } GET /cgi-bin/chunked.sh HTTP/1.1 Host: localhost Connection: close --- HTTP/1.1 200 OK Date: Wed, 25 Aug 2010 12:11:44 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT Etag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Content-Length: 0 Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Content-Length: 0 string(3) "1.1" bool(true) int(200) string(2) "OK" array(10) { ["Date"]=> string(29) "Wed, 25 Aug 2010 12:11:44 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Last-Modified"]=> string(29) "Wed, 28 Apr 2010 10:54:37 GMT" ["Etag"]=> string(23) ""2002a-0-48549d615a35c"" ["Accept-Ranges"]=> string(5) "bytes" ["Content-Length"]=> string(1) "0" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Content-Length"]=> string(1) "0" } GET /default/empty.txt HTTP/1.1 Host: localhost Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 09:55:09 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Last-Modified: Wed, 28 Apr 2010 10:54:37 GMT Etag: "2002a-0-48549d615a35c" Accept-Ranges: bytes Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Content-Length: 20 X-Original-Content-Encoding: gzip string(3) "1.1" bool(true) int(200) string(2) "OK" array(10) { ["Date"]=> string(29) "Thu, 26 Aug 2010 09:55:09 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Last-Modified"]=> string(29) "Wed, 28 Apr 2010 10:54:37 GMT" ["Etag"]=> string(23) ""2002a-0-48549d615a35c"" ["Accept-Ranges"]=> string(5) "bytes" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Content-Length"]=> string(2) "20" ["X-Original-Content-Encoding"]=> string(4) "gzip" } GET /default/empty.txt HTTP/1.1 Host: localhost Accept-Encoding: gzip Connection: close HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 11:41:02 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 X-Powered-By: PHP/5.3.3 Vary: Accept-Encoding Connection: close Content-Type: text/html X-Original-Transfer-Encoding: chunked Content-Length: 0 string(3) "1.1" bool(true) int(200) string(2) "OK" array(8) { ["Date"]=> string(29) "Thu, 26 Aug 2010 11:41:02 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["X-Powered-By"]=> string(9) "PHP/5.3.3" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(9) "text/html" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(0) } GET /default/empty.php HTTP/1.1 Connection: close Host: localhost HTTP/1.1 200 OK Date: Thu, 26 Aug 2010 12:51:28 GMT Server: Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6 Vary: Accept-Encoding Connection: close Content-Type: text/plain X-Original-Transfer-Encoding: chunked Content-Length: 14 Hello, World! string(3) "1.1" bool(true) int(200) string(2) "OK" array(7) { ["Date"]=> string(29) "Thu, 26 Aug 2010 12:51:28 GMT" ["Server"]=> string(68) "Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/1.0.0a mod_fastcgi/2.4.6" ["Vary"]=> string(15) "Accept-Encoding" ["Connection"]=> string(5) "close" ["Content-Type"]=> string(10) "text/plain" ["X-Original-Transfer-Encoding"]=> string(7) "chunked" ["Content-Length"]=> int(14) } GET /cgi-bin/chunked.sh HTTP/1.1 Host: localhost Connection: close Done --TEST-- env request message --SKIPIF-- --POST_RAW-- Content-Type: test/something b=c --ENV-- HTTP_X_TEST=test --COOKIE-- foo=bar --INI-- always_populate_raw_post_data=-1 --FILE-- toString\n"; echo $m,"\n"; echo "Body->toString\n"; var_dump((string)$m->getBody()); echo "stream\n"; var_dump(file_get_contents("php://input")); ?> Done --EXPECTF-- Test object(%s)#%d (13) { ["type":protected]=> int(1) ["body":protected]=> NULL ["requestMethod":protected]=> string(4) "POST" ["requestUrl":protected]=> string(0) "" ["responseStatus":protected]=> string(0) "" ["responseCode":protected]=> int(0) ["httpVersion":protected]=> string(3) "1.1" ["headers":protected]=> array(4) { ["X-Test"]=> string(4) "test" ["Content-Length"]=> string(1) "3" ["Content-Type"]=> string(14) "test/something" ["Cookie"]=> string(7) "foo=bar" } ["parentMessage":protected]=> NULL ["query":protected]=> object(http\QueryString)#%d (1) { ["queryArray":"http\QueryString":private]=> array(0) { } } ["form":protected]=> object(http\QueryString)#%d (1) { ["queryArray":"http\QueryString":private]=> array(0) { } } ["cookie":protected]=> object(http\QueryString)#%d (1) { ["queryArray":"http\QueryString":private]=> array(1) { ["foo"]=> string(3) "bar" } } ["files":protected]=> array(0) { } } Message->toString POST / HTTP/1.1%a X-Test: test%a Content-Length: 3%a Content-Type: test/something%a Cookie: foo=bar%a %a b=c Body->toString string(3) "b=c" stream string(3) "b=c" Done --TEST-- multipart message --SKIPIF-- --FILE-- isMultipart($boundary)) { var_dump($boundary); foreach ($m->splitMultipartBody() as $i => $mm) { echo "==$i==\n",$mm,"===\n"; } } ?> DONE --EXPECTF-- string(40) "----------------------------6e182425881c" ==%d== Content-Disposition: form-data; name="composer"; filename="composer.json" Content-Type: application/octet-stream Content-Length: 567 { "name": "mike_php_net/autocracy", "type": "library", "description": "http\\Controller preserves your autocracy", "keywords": ["http", "controller", "pecl", "pecl_http"], "homepage": "http://github.com/mike-php-net/autocracy", "license": "BSD-2", "authors": [ { "name": "Michael Wallner", "email": "mike@php.net" } ], "require": { "php": ">=5.4.0", "pecl/pecl_http": "2.*" }, "autoload": { "psr-0": { "http\\Controller": "lib" } } } === ==%d== Content-Disposition: form-data; name="LICENSE"; filename="LICENSE" Content-Type: application/octet-stream Content-Length: 1354 Copyright (c) 2011-2012, Michael Wallner . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. === DONE --TEST-- message reversal --SKIPIF-- --FILE-- toString(true); echo "===\n"; echo newHttpMessage($s)->reverse()->toString(true); $m = newHttpMessage($s); $r = $m->reverse(); unset($m); var_dump($r->count()); echo $r->toString(true); ?> DONE --EXPECTF-- GET /first HTTP/1.1 HTTP/1.1 200 Ok-first GET /second HTTP/1.1 HTTP/1.1 200 Ok-second GET /third HTTP/1.1 HTTP/1.1 200 Ok-third === HTTP/1.1 200 Ok-third GET /third HTTP/1.1 HTTP/1.1 200 Ok-second GET /second HTTP/1.1 HTTP/1.1 200 Ok-first GET /first HTTP/1.1 int(6) HTTP/1.1 200 Ok-third GET /third HTTP/1.1 HTTP/1.1 200 Ok-second GET /second HTTP/1.1 HTTP/1.1 200 Ok-first GET /first HTTP/1.1 DONE --TEST-- message cloning --SKIPIF-- setType(http\Message::TYPE_REQUEST); $cpy->setHeaders(array("Numbers" => array(1,2,3,4.5))); echo $msg; echo "\n===\n"; echo $cpy; ?> DONE --EXPECTF-- HTTP/1.1 200 Ok String: foobar === UNKNOWN / HTTP/1.1 Numbers: 1, 2, 3, 4.5 DONE --TEST-- message var_dump with inherited property with increased access level --SKIPIF-- --FILE-- headers["foo"] = "bar"; var_dump($m); ?> DONE --EXPECTF-- Test object(c)#%d (9) { ["headers"]=> array(1) { ["foo"]=> string(3) "bar" } ["type":protected]=> int(0) ["body":protected]=> NULL ["requestMethod":protected]=> string(0) "" ["requestUrl":protected]=> string(0) "" ["responseStatus":protected]=> string(0) "" ["responseCode":protected]=> int(0) ["httpVersion":protected]=> string(3) "1.1" ["parentMessage":protected]=> NULL } DONE --TEST-- message to stream --SKIPIF-- --FILE-- addHeader("Content-Type", "text/plain"); $m->getBody()->append("this\nis\nthe\ntext"); $f = tmpfile(); $m->toStream($f); rewind($f); var_dump((string) $m === stream_get_contents($f)); fclose($f); ?> Done --EXPECT-- Test bool(true) Done --TEST-- message to callback --SKIPIF-- --FILE-- addHeader("Content-Type", "text/plain"); $m->getBody()->append("this\nis\nthe\ntext"); $d = new http\Encoding\Stream\Deflate; $s = ""; $m->toCallback(function ($m, $data) use ($d, &$s) { $s.=$d->update($data); }); $s.=$d->finish(); var_dump($m->toString() === http\Encoding\Stream\Inflate::decode($s)); ?> Done --EXPECT-- Test bool(true) Done --TEST-- message properties --SKIPIF-- --FILE-- $prop; case "set": $this->$prop = current($args); break; } } } } $test = new message; var_dump(0 === $test->testGetType()); var_dump(null === $test->testGetBody()); var_dump(null === $test->testGetRequestMethod()); var_dump(null === $test->testGetRequestUrl()); var_dump(null === $test->testGetResponseStatus()); var_dump(null === $test->testGetResponseCode()); var_dump("1.1" === $test->testGetHttpVersion()); var_dump(array() === $test->testGetHeaders()); var_dump(null === $test->testGetParentMessage()); $test->testSetType(http\Message::TYPE_REQUEST); var_dump(http\Message::TYPE_REQUEST === $test->testGetType()); var_dump(http\Message::TYPE_REQUEST === $test->getType()); $body = new http\Message\Body; $test->testSetBody($body); var_dump($body === $test->testGetBody()); var_dump($body === $test->getBody()); $file = fopen(__FILE__,"r"); $test->testSetBody($file); var_dump($file === $test->testGetBody()->getResource()); var_dump($file === $test->getBody()->getResource()); $test->testSetBody("data"); var_dump("data" === (string) $test->testGetBody()); var_dump("data" === (string) $test->getBody()); $test->testSetRequestMethod("HEAD"); var_dump("HEAD" === $test->testGetRequestMethod()); var_dump("HEAD" === $test->getRequestMethod()); $test->testSetRequestUrl("/"); var_dump("/" === $test->testGetRequestUrl()); var_dump("/" === $test->getRequestUrl()); var_dump("HEAD / HTTP/1.1" === $test->getInfo()); $test->testSetType(http\Message::TYPE_RESPONSE); $test->setResponseStatus("Created"); var_dump("Created" === $test->testGetResponseStatus()); var_dump("Created" === $test->getResponseStatus()); $test->setResponseCode(201); var_dump(201 === $test->testGetResponseCode()); var_dump(201 === $test->getResponseCode()); $test->testSetResponseStatus("Ok"); var_dump("Ok" === $test->testGetResponseStatus()); var_dump("Ok" === $test->getResponseStatus()); $test->testSetResponseCode(200); var_dump(200 === $test->testGetResponseCode()); var_dump(200 === $test->getResponseCode()); $test->testSetHttpVersion("1.0"); var_dump("1.0" === $test->testGetHttpVersion()); var_dump("1.0" === $test->getHttpVersion()); var_dump("HTTP/1.0 200 OK" === $test->getInfo()); $test->setHttpVersion("1.1"); var_dump("1.1" === $test->testGetHttpVersion()); var_dump("1.1" === $test->getHttpVersion()); var_dump("HTTP/1.1 200 OK" === $test->getInfo()); $test->setInfo("HTTP/1.1 201 Created"); var_dump("Created" === $test->testGetResponseStatus()); var_dump("Created" === $test->getResponseStatus()); var_dump(201 === $test->testGetResponseCode()); var_dump(201 === $test->getResponseCode()); var_dump("1.1" === $test->testGetHttpVersion()); var_dump("1.1" === $test->getHttpVersion()); $test->testSetHeaders(array("Foo" => "bar")); var_dump(array("Foo" => "bar") === $test->testGetHeaders()); var_dump(array("Foo" => "bar") === $test->getHeaders()); var_dump("bar" === $test->getHeader("foo")); var_dump(false === $test->getHeader("bar")); $parent = new message; $test->testSetParentMessage($parent); var_dump($parent === $test->testGetParentMessage()); var_dump($parent === $test->getParentMessage()); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) bool(true) Done --TEST-- message body --SKIPIF-- --FILE-- append("foo"); $m->addBody($body); $body = new http\Message\Body; $body->append("bar"); $m->addBody($body); var_dump("foobar" === (string) $m->getBody()); ?> Done --EXPECT-- Test bool(true) Done --TEST-- message headers --SKIPIF-- --INI-- date.timezone=UTC --FILE-- str = $str; } function __toString() { return (string) $this->str; } } $m = new http\Message; $m->addHeaders(array("foo"=>"bar","bar"=>"foo")); var_dump(array("Foo"=>"bar", "Bar"=>"foo") === $m->getHeaders()); $m->addHeaders(array("key"=>"val","more"=>"Stuff")); var_dump(array("Foo"=>"bar", "Bar"=>"foo","Key"=>"val","More"=>"Stuff") === $m->getHeaders()); $m = new http\Message("GET / HTTP/1.1"); $m->addHeader("Accept", "text/html"); $m->addHeader("Accept", "text/xml;q=0"); $m->addHeader("Accept", "text/plain;q=0.5"); var_dump( "GET / HTTP/1.1\r\n". "Accept: text/html, text/xml;q=0, text/plain;q=0.5\r\n" === $m->toString() ); $m = new http\Message("HTTP/1.1 200 Ok"); $m->addHeader("Bool", true); $m->addHeader("Int", 123); $m->addHeader("Float", 1.23); $m->addHeader("Array", array(1,2,3)); $m->addHeader("Object", new strval("test")); $m->addHeader("Set-Cookie", array( array( "cookies" => array("foo" => "bar"), "expires" => date_create("2012-12-31 22:59:59 GMT")->format( DateTime::COOKIE ), "path" => "/somewhere" ) ) ); $m->addHeader("Set-Cookie", "val=0"); var_dump( "HTTP/1.1 200 Ok\r\n". "Bool: true\r\n". "Int: 123\r\n". "Float: 1.23\r\n". "Array: 1, 2, 3\r\n". "Object: test\r\n". "Set-Cookie: foo=bar; path=/somewhere; expires=Mon, 31 Dec 2012 22:59:59 GMT; \r\n". "Set-Cookie: val=0\r\n" === $m->toString() ); ?> Done --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) Done --TEST-- message part --SKIPIF-- --FILE-- addHeader("Content-Type", "text/plain"); $p->getBody()->append("data"); $m = new http\Message("HTTP/1.1 200"); $m->getBody()->addPart($p); echo $m; ?> Done --EXPECTF-- Test HTTP/1.1 200 Content-Length: %d Content-Type: multipart/form-data; boundary="%x.%x" --%x.%x Content-Type: text/plain Content-Length: 4 data --%x.%x-- Done --TEST-- message detach --SKIPIF-- --FILE-- detach(); var_dump(3 === count($m)); var_dump(1 === count($d)); var_dump("HTTP/1.1 302 Found\r\n\r\n" === $d->toString(true)); ?> Done --EXPECTF-- Test bool(true) bool(true) bool(true) bool(true) Done --TEST-- message prepend --SKIPIF-- --FILE-- prepend($p); $p = $m; } var_dump( "HTTP/1.1 200\r\n\r\n". "HTTP/1.1 201\r\n\r\n". "HTTP/1.1 202\r\n\r\n". "HTTP/1.1 203\r\n\r\n". "HTTP/1.1 204\r\n\r\n". "HTTP/1.1 205\r\n\r\n". "HTTP/1.1 206\r\n\r\n". "HTTP/1.1 207\r\n\r\n". "HTTP/1.1 208\r\n\r\n" === $m->toString(true) ); ?> Done --EXPECTF-- Test bool(true) Done --TEST-- message errors --SKIPIF-- --FILE-- setRequestUrl("/foo"); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } $m->setType(http\Message::TYPE_REQUEST); try { $m->setRequestUrl(""); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } $m = new http\Message; try { $m->getParentMessage(); die("unreached"); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } $m = new http\Message("HTTP/1.1 200\r\nHTTP/1.1 201"); try { $m->prepend($m->getParentMessage()); die("unreached"); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } ?> Done --EXPECTF-- Test http\Message is not of type request Cannot set http\Message's request url to an empty string http\Message has not parent message Cannot prepend a message located within the same message chain Done --TEST-- message content range --SKIPIF-- --FILE-- ===DONE=== --EXPECT-- Test PUT / HTTP/1.1 User-Agent: PECL_HTTP/2.3.0dev PHP/5.6.6-dev libcurl/7.41.0-DEV Host: localhost:8000 Accept: */* Expect: 100-continue Content-Length: 3 Content-Range: bytes 1-2/3 X-Original-Content-Length: 3 23===DONE=== --TEST-- message body stat --SKIPIF-- --FILE-- stat("size")); var_dump(filemtime(__FILE__) === $file->stat("mtime")); var_dump(fileatime(__FILE__) === $file->stat("atime")); var_dump(filectime(__FILE__) === $file->stat("ctime")); var_dump( (object) array( "size" => 0, "mtime" => 0, "atime" => 0, "ctime" => 0, ) == $temp->stat() ); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) bool(true) bool(true) DONE --TEST-- message body append --SKIPIF-- --FILE-- append("yes"); var_dump((string) $temp); ?> DONE --EXPECT-- Test string(3) "yes" DONE --TEST-- message body append error --SKIPIF-- --FILE-- append("nope"); } catch (Exception $e) { echo $e, "\n"; } ?> DONE --EXPECTF-- Test exception 'http\Exception\RuntimeException' with message 'http\Message\Body::append(): Failed to append 4 bytes to body; wrote 0' in %s:%d Stack trace: #0 %s(%d): http\Message\Body->append('nope') #1 {main} DONE --TEST-- message body add form --SKIPIF-- --FILE-- addForm( array( "foo" => "bar", "more" => array( "bah", "baz", "fuz" ), ), array( array( "file" => __FILE__, "name" => "upload", "type" => "text/plain", ) ) ); echo $temp; ?> DONE --EXPECTF-- Test --%x.%x Content-Disposition: form-data; name="foo" bar --%x.%x Content-Disposition: form-data; name="more[0]" bah --%x.%x Content-Disposition: form-data; name="more[1]" baz --%x.%x Content-Disposition: form-data; name="more[2]" fuz --%x.%x Content-Disposition: form-data; name="upload"; filename="%s" Content-Transfer-Encoding: binary Content-Type: text/plain addForm( array( "foo" => "bar", "more" => array( "bah", "baz", "fuz" ), ), array( array( "file" => __FILE__, "name" => "upload", "type" => "text/plain", ) ) ); echo $temp; ?> DONE --%x.%x-- DONE --TEST-- message body add part --SKIPIF-- --FILE-- getBoundary()); $temp->addPart(new http\Message("This: is a header\n\nand this is the data\n")); var_dump($temp->getBoundary()); echo $temp; ?> DONE --EXPECTF-- Test NULL string(%d) "%x.%x" --%x.%x This: is a header Content-Length: 21 and this is the data --%x.%x-- DONE --TEST-- message body etag --SKIPIF-- --INI-- http.etag.mode = crc32b --FILE-- etag() ); var_dump($temp->etag()); ?> DONE --EXPECT-- Test bool(true) string(8) "00000000" DONE --TEST-- message body to stream --SKIPIF-- --FILE-- toStream($f = fopen("php://temp", "w")); fseek($f, 0, SEEK_SET); var_dump(file_get_contents(__FILE__) === stream_get_contents($f)); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- message body to callback --SKIPIF-- --FILE-- toCallback( function($body, $string) use (&$s) { $s.=$string; } ); var_dump($s === (string) $file); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- message body clone --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) DONE --TEST-- message body resource --SKIPIF-- --FILE-- getResource(); var_dump(is_resource($stream)); $stat = fstat($stream); var_dump(filesize(__FILE__) === $stat["size"]); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- message parser --SKIPIF-- --FILE-- parse(fgets($fd), 0, $message)) { case Parser::STATE_DONE: $string = (string) $message; break 2; case Parser::STATE_FAILURE: throw new Exception(($e = error_get_last()) ? $e["message"] : "Could not parse $file"); } } if (!$string) { $s = array("START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "UPDATE_CL", "DONE"); printf("Unexpected state: %s (%s)\n", $s[$parser->getState()], $file); } $parser = new Parser; rewind($fd); unset($message); switch ($parser->stream($fd, 0, $message)) { case Parser::STATE_DONE: case Parser::STATE_START: break; default: printf("Expected parser state 0 or 8, got %d", $parser->getState()); } if ($string !== (string) $message) { $a = explode("\n", $string); $b = explode("\n", (string) $message); do { $aa = array_shift($a); $bb = array_shift($b); if ($aa !== $bb) { isset($aa) and printf("-- %s\n", $aa); isset($bb) and printf("++ %s\n", $bb); } } while ($a || $b); } } ?> DONE --EXPECT-- Test DONE --TEST-- message parser with nonblocking stream --SKIPIF-- --FILE-- stream($socket[0], 0, $msg); fwrite($socket[1], $line); $parser->stream($socket[0], 0, $msg); } var_dump($msg, (string) $msg->getBody()); ?> DONE --EXPECTF-- Test object(http\Message)#%d (9) { ["type":protected]=> int(1) ["body":protected]=> object(http\Message\Body)#2 (0) { } ["requestMethod":protected]=> string(3) "GET" ["requestUrl":protected]=> string(1) "/" ["responseStatus":protected]=> string(0) "" ["responseCode":protected]=> int(0) ["httpVersion":protected]=> string(3) "1.1" ["headers":protected]=> array(3) { ["Host"]=> string(9) "localhost" ["Content-Length"]=> string(1) "3" ["X-Original-Content-Length"]=> string(1) "3" } ["parentMessage":protected]=> NULL } string(3) "OK " DONE--TEST-- negotiate --SKIPIF-- --ENV-- HTTP_ACCEPT=text/html,text/plain,text/xml;q=0.1,image/*;q=0.1,*/*;q=0 HTTP_ACCEPT_CHARSET=utf-8,iso-8859-1;q=0.8,iso-8859-15;q=0 HTTP_ACCEPT_ENCODING=gzip,deflate;q=0 HTTP_ACCEPT_LANGUAGE=de-DE,de-AT;q=0.9,en;q=0.8,fr;q=0 --FILE-- CONTENT TYPE CHARSET ENCODING LANGUAGE CUSTOM DONE --EXPECT-- CONTENT TYPE text/html: Array ( [text/html] => 0.99 [text/xml] => 0.1 ) text/xml: Array ( [text/xml] => 0.1 ) text/json: Array ( ) CHARSET utf-8: Array ( [utf-8] => 0.99 [iso-8859-1] => 0.8 ) iso-8859-1: Array ( [iso-8859-1] => 0.8 ) utf-16: Array ( ) ENCODING gzip: Array ( [gzip] => 0.99 ) : Array ( ) LANGUAGE de: Array ( [de] => 0.99 [en] => 0.8 ) de-DE: Array ( [de-DE] => 0.99 [de-AT] => 0.9 [en] => 0.8 ) en: Array ( [en] => 0.8 ) CUSTOM a.b: Array ( [a.b] => 0.9 [c.e] => 0.1 [a.x] => 0.1 ) DONE --TEST-- header params --SKIPIF-- --FILE-- array("charset" => "iso-8859-1")); var_dump( isset($ct["text/html"]), isset($ct["text/json"]), $ct["text/json"]["arguments"]["charset"] ); var_dump((string) $ct,$ct); ?> DONE --EXPECTF-- Test bool(true) bool(false) string(5) "utf-8" bool(false) bool(true) string(10) "iso-8859-1" string(%d) "text/json;charset=iso-8859-1" object(http\Params)#%d (5) { ["params"]=> array(1) { ["text/json"]=> array(2) { ["value"]=> bool(true) ["arguments"]=> array(1) { ["charset"]=> string(10) "iso-8859-1" } } } ["param_sep"]=> string(1) "," ["arg_sep"]=> string(1) ";" ["val_sep"]=> string(1) "=" ["flags"]=> int(0) } DONE --TEST-- query parser --SKIPIF-- --FILE-- DONE --EXPECTF-- object(http\Params)#%d (5) { ["params"]=> array(2) { ["foo"]=> array(2) { ["value"]=> string(3) "bar" ["arguments"]=> array(0) { } } ["arr"]=> array(2) { ["value"]=> array(2) { [0]=> string(1) "1" [1]=> string(1) "2" } ["arguments"]=> array(0) { } } } ["param_sep"]=> array(2) { [0]=> string(1) "&" [1]=> string(1) ";" } ["arg_sep"]=> string(0) "" ["val_sep"]=> string(1) "=" ["flags"]=> int(12) } foo=bar&arr%5B0%5D=1&arr%5B1%5D=2 DONE --TEST-- default params --SKIPIF-- --FILE-- "arg", "bar"=>"bla", "gotit"=>"now"); $r = array ( 'foo' => array ( 'value' => true, 'arguments' => array ( ), ), 'bar' => array ( 'value' => true, 'arguments' => array ( 'arg' => '0', 'bla' => true, ), ), 'gotit' => array ( 'value' => '0', 'arguments' => array ( 'now' => true, ), ), ); # --- var_dump(count($p->params)); echo "key exists\n"; foreach ($k as $key) { var_dump(array_key_exists($key, $p->params)); } echo "values\n"; foreach ($k as $key) { var_dump($p[$key]["value"]); } echo "args\n"; foreach ($k as $key) { var_dump(count($p[$key]["arguments"])); } echo "arg values\n"; foreach ($k as $key) { var_dump(@$p[$key]["arguments"][$a[$key]]); } echo "equals\n"; var_dump($c === (string) $p); var_dump($r === $p->params); $x = new http\Params($p->params); var_dump($r === $x->toArray()); ?> DONE --EXPECT-- Test int(3) key exists bool(true) bool(true) bool(true) values bool(true) bool(true) string(1) "0" args int(0) int(2) int(1) arg values NULL bool(true) bool(true) equals bool(true) bool(true) bool(true) DONE --TEST-- custom params --SKIPIF-- --FILE-- "arg", "bar"=>"bla", "gotit"=>"now"); $r = array ( 'foo' => array ( 'value' => true, 'arguments' => array ( ), ), 'bar' => array ( 'value' => true, 'arguments' => array ( 'arg' => '0', 'bla' => true, ), ), 'gotit' => array ( 'value' => '0', 'arguments' => array ( 'now' => true, ), ), ); # --- var_dump(count($p->params)); echo "key exists\n"; foreach ($k as $key) { var_dump(array_key_exists($key, $p->params)); } echo "values\n"; foreach ($k as $key) { var_dump($p[$key]["value"]); } echo "args\n"; foreach ($k as $key) { var_dump(count($p[$key]["arguments"])); } echo "arg values\n"; foreach ($k as $key) { var_dump(@$p[$key]["arguments"][$a[$key]]); } echo "equals\n"; var_dump($c === (string) $p); var_dump($r === $p->params); $x = new http\Params($p->params); var_dump($r === $x->toArray()); ?> DONE --EXPECT-- Test int(3) key exists bool(true) bool(true) bool(true) values bool(true) bool(true) string(1) "0" args int(0) int(2) int(1) arg values NULL bool(true) bool(true) equals bool(true) bool(true) bool(true) DONE --TEST-- quoted params --SKIPIF-- --FILE-- array( "value" => true, "arguments" => array( "boundary" => "--123" ) ) ); var_dump($c === $p->params); var_dump("multipart/form-data;boundary=--123" === (string) $p); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- escaped params --SKIPIF-- --FILE-- array( "value" => true, "arguments" => array( "name" => "upload", "filename" => "trick\"\0\"ed" ) ) ); var_dump($c === $p->params); var_dump("form-data;name=upload;filename=\"trick\\\"\\000\\\"ed\"" === (string) $p); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- urlencoded params --SKIPIF-- --FILE-- array( "value" => "b\"r", "arguments" => array(), ), "bar" => array( "value" => "b\"z", "arguments" => array(), ), "a[][]" => array( "value" => "1", "arguments" => array(), ), ); var_dump($c === $p->params); var_dump("foo=b%22r&bar=b%22z&a%5B%5D%5B%5D=1" === (string) $p); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- querystring params --SKIPIF-- --FILE-- array( "value" => "b\"r", "arguments" => array(), ), "bar" => array( "value" => "b\"z", "arguments" => array(), ), "a" => array( "value" => array( array("1") ), "arguments" => array(), ), ); var_dump($c === $p->params); var_dump("foo=b%22r&bar=b%22z&a%5B0%5D%5B0%5D=1" === (string) $p); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- empty params --SKIPIF-- --FILE-- params); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- int key params --SKIPIF-- --FILE-- array("value" => "nothing", "arguments" => array(1=>"yes"))) === $p->params); var_dump("0=nothing;1=yes" === $p->toString()); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- bool args params --SKIPIF-- --FILE-- false, "arguments" => array("wrong" => false, "correct" => true)); $p["container"] = $container; var_dump("container=0;wrong=0;correct" === $p->toString()); var_dump(array("container" => $container) === $p->toArray()); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- no args params --SKIPIF-- --FILE-- toString()); $p["param"] = false; var_dump("param=0" === $p->toString()); ?> DONE --EXPECT-- Test bool(true) bool(true) DONE --TEST-- header params rfc5987 --SKIPIF-- --FILE-- params, (string) $p); $p = new http\Params("bar; title*=iso-8859-1'en'%A3%20rates"); var_dump($p->params, (string) $p); $p = new http\Params("bar; title*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates"); var_dump($p->params, (string) $p); ?> ===DONE=== --EXPECT-- Test array(1) { ["attachment"]=> array(2) { ["value"]=> bool(true) ["arguments"]=> array(1) { ["*rfc5987*"]=> array(1) { ["filename"]=> array(1) { [""]=> string(10) "döner.pdf" } } } } } string(42) "attachment;filename*=utf-8''d%C3%B6ner.pdf" array(1) { ["bar"]=> array(2) { ["value"]=> bool(true) ["arguments"]=> array(1) { ["*rfc5987*"]=> array(1) { ["title"]=> array(1) { ["en"]=> string(8) "ÂŁ rates" } } } } } string(34) "bar;title*=utf-8'en'%C2%A3%20rates" array(1) { ["bar"]=> array(2) { ["value"]=> bool(true) ["arguments"]=> array(1) { ["*rfc5987*"]=> array(1) { ["title"]=> array(1) { [""]=> string(16) "ÂŁ and € rates" } } } } } string(50) "bar;title*=utf-8''%C2%A3%20and%20%E2%82%AC%20rates" ===DONE=== --TEST-- header params rfc5987 --SKIPIF-- --FILE-- params); var_dump((string)$p === $t, (string)$p, $t); ?> ===DONE=== --EXPECT-- Test array(3) { ["p1"]=> array(2) { ["*rfc5987*"]=> array(1) { [""]=> string(5) "sĂŒĂŸ" } ["arguments"]=> array(0) { } } ["p2"]=> array(2) { ["*rfc5987*"]=> array(1) { [""]=> string(5) "heiß" } ["arguments"]=> array(2) { ["*rfc5987*"]=> array(2) { ["a1"]=> array(1) { [""]=> string(3) "aß" } ["a2"]=> array(1) { [""]=> string(3) "eß" } } ["a3"]=> string(2) "no" } } ["p3"]=> array(2) { ["value"]=> string(3) "not" ["arguments"]=> array(0) { } } } bool(true) string(96) "p1*=utf-8''s%C3%BC%C3%9F,p2*=utf-8''hei%C3%9F;a1*=utf-8''a%C3%9F;a2*=utf-8''e%C3%9F;a3=no,p3=not" string(96) "p1*=utf-8''s%C3%BC%C3%9F,p2*=utf-8''hei%C3%9F;a1*=utf-8''a%C3%9F;a2*=utf-8''e%C3%9F;a3=no,p3=not" ===DONE=== --TEST-- header params rfc5987 regression --SKIPIF-- --FILE-- array("filename"=>"foo.bar"))); var_dump($p->params); var_dump((string)$p); ?> ===DONE=== --EXPECT-- Test array(1) { ["attachment"]=> array(1) { ["filename"]=> string(7) "foo.bar" } } string(27) "attachment;filename=foo.bar" ===DONE=== --TEST-- phpinfo --SKIPIF-- --FILE-- Done --EXPECTF-- Test %a HTTP Support => enabled Extension Version => 2.%s %a Done --TEST-- property proxy --SKIPIF-- --XFAIL-- TBD --FILE-- headers["bykey"] = 1; var_dump($this->headers); $h = &$this->headers; $h["by1ref"] = 2; var_dump($this->headers); $x = &$this->headers["byXref"]; $h = &$this->headers["by2ref"]; $h = 1; var_dump($this->headers); $x = 2; var_dump($this->headers); $this->headers["bynext"][] = 1; $this->headers["bynext"][] = 2; $this->headers["bynext"][] = 3; var_dump($this->headers); } } $m=new m; $m->test(); echo $m,"\n"; ?> DONE --EXPECTF-- array(1) { ["bykey"]=> int(1) } array(2) { ["bykey"]=> int(1) ["by1ref"]=> int(2) } array(3) { ["bykey"]=> int(1) ["by1ref"]=> int(2) ["by2ref"]=> &int(1) } array(4) { ["bykey"]=> int(1) ["by1ref"]=> int(2) ["by2ref"]=> &int(1) ["byXref"]=> &int(2) } array(5) { ["bykey"]=> int(1) ["by1ref"]=> int(2) ["by2ref"]=> &int(1) ["byXref"]=> &int(2) ["bynext"]=> array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } } bykey: 1 by1ref: 2 by2ref: 1 byXref: 2 bynext: 1, 2, 3 DONE --TEST-- query string --SKIPIF-- --GET-- str=abc&num=-123&dec=123.123&bool=1&arr[]=1&arr[]=2&ma[l1][l2]=2&ma[l2][l3][l4]=3 --FILE-- getString("str")); var_dump($q->getInt("num")); var_dump($q->getFloat("dec")); var_dump($q->getInt("dec")); var_dump($q->getFloat("dec")); var_dump($q->getBool("bool")); var_dump($q->getInt("bool")); var_dump($q->getBool("num")); var_dump($q->getInt("num")); var_dump($q->getArray("arr")); var_dump($q->getArray("ma")); var_dump($q->getObject("arr")); var_dump($q->getObject("ma")); $s = $q->toString(); printf("\nClone modifications do not alter global instance:\n"); $q->mod(array("arr" => array(3 => 3))); printf("%s\n", $q); printf("\nClone modifications do not alter standard instance:\n"); $q2 = new http\QueryString($s); $q3 = $q2->mod(array("arr" => array(3 => 3))); printf("%s\n%s\n", $q2, $q3); #var_dump($q2, $q3); printf("\nIterator:\n"); $it = new RecursiveIteratorIterator($q2, RecursiveIteratorIterator::SELF_FIRST); foreach ($it as $k => $v) { $i = $it->getDepth()*8; @printf("%{$i}s: %s\n", $k, $v); } printf("\nReplace a multi dimensional key:\n"); printf("%s\n", $q2->mod(array("ma" => null))->set(array("ma" => array("l1" => false)))); printf("\nXlate:\n"); $qu = new http\QueryString("ĂŒ=ö"); printf("utf8: %s\n", $qu); printf("latin1: %s\n", method_exists($qu, "xlate") ? $qu->xlate("utf-8", "latin1") : "%FC=%F6"); printf("\nOffsets:\n"); var_dump($q2["ma"]); $q2["ma"] = array("bye"); var_dump($q2["ma"]); var_dump(isset($q2["ma"])); unset($q2["ma"]); var_dump(isset($q2["ma"])); echo "Done\n"; ?> --EXPECTF-- Test Global instance: str=abc&num=-123&dec=123.123&bool=1&arr%5B0%5D=1&arr%5B1%5D=2&ma%5Bl1%5D%5Bl2%5D=2&ma%5Bl2%5D%5Bl3%5D%5Bl4%5D=3 Standard getters: string(3) "abc" int(-123) float(123.123) int(123) float(123.123) bool(true) int(1) bool(true) int(-123) array(2) { [0]=> string(1) "1" [1]=> string(1) "2" } array(2) { ["l1"]=> array(1) { ["l2"]=> string(1) "2" } ["l2"]=> array(1) { ["l3"]=> array(1) { ["l4"]=> string(1) "3" } } } object(stdClass)#%d (2) { [0]=> string(1) "1" [1]=> string(1) "2" } object(stdClass)#%d (2) { ["l1"]=> array(1) { ["l2"]=> string(1) "2" } ["l2"]=> array(1) { ["l3"]=> array(1) { ["l4"]=> string(1) "3" } } } Clone modifications do not alter global instance: str=abc&num=-123&dec=123.123&bool=1&arr%5B0%5D=1&arr%5B1%5D=2&ma%5Bl1%5D%5Bl2%5D=2&ma%5Bl2%5D%5Bl3%5D%5Bl4%5D=3 Clone modifications do not alter standard instance: str=abc&num=-123&dec=123.123&bool=1&arr%5B0%5D=1&arr%5B1%5D=2&ma%5Bl1%5D%5Bl2%5D=2&ma%5Bl2%5D%5Bl3%5D%5Bl4%5D=3 str=abc&num=-123&dec=123.123&bool=1&arr%5B0%5D=1&arr%5B1%5D=2&arr%5B3%5D=3&ma%5Bl1%5D%5Bl2%5D=2&ma%5Bl2%5D%5Bl3%5D%5Bl4%5D=3 Iterator: str: abc num: -123 dec: 123.123 bool: 1 arr: Array 0: 1 1: 2 ma: Array l1: Array l2: 2 l2: Array l3: Array l4: 3 Replace a multi dimensional key: str=abc&num=-123&dec=123.123&bool=1&arr%5B0%5D=1&arr%5B1%5D=2&ma%5Bl1%5D= Xlate: utf8: %C3%BC=%C3%B6 latin1: %FC=%F6 Offsets: array(2) { ["l1"]=> array(1) { ["l2"]=> string(1) "2" } ["l2"]=> array(1) { ["l3"]=> array(1) { ["l4"]=> string(1) "3" } } } array(1) { [0]=> string(3) "bye" } bool(true) bool(false) Done --TEST-- query string --SKIPIF-- --FILE-- get()); printf("Get defval\n"); var_dump("nonexistant" === $q->get("unknown", "s", "nonexistant")); var_dump(null === $q->get("unknown")); printf("Get 'a'\n"); var_dump("b" === $q->get("a")); var_dump(0 === $q->get("a", "i")); var_dump(array("b") === $q->get("a", "a")); var_dump((object)array("scalar" => "b") == $q->get("a", "o")); printf("Get 'r'\n"); var_dump(array("0","1","2") === $q->get("r")); printf("Get 'rr'\n"); var_dump(array(array("00","01")) === $q->get("rr")); printf("Get 1\n"); var_dump(2 == $q->get(1)); var_dump("2" === $q->get(1, "s")); var_dump(2.0 === $q->get(1, "f")); var_dump($q->get(1, "b")); printf("Del 'a'\n"); var_dump("b" === $q->get("a", http\QueryString::TYPE_STRING, null, true)); var_dump(null === $q->get("a")); printf("Del all\n"); $q->set(array("a" => null, "r" => null, "rr" => null, 1 => null)); var_dump("" === $q->toString()); $q = new http\QueryString($s); printf("QSO\n"); var_dump($e === (string) new http\QueryString($q)); var_dump(http_build_query(array("e"=>$q->toArray())) === (string) new http\QueryString(array("e" => $q))); printf("Iterator\n"); var_dump($q->toArray() === iterator_to_array($q)); printf("Serialize\n"); var_dump($e === (string) unserialize(serialize($q))); ?> DONE --EXPECT-- Test bool(true) bool(true) Get defval bool(true) bool(true) Get 'a' bool(true) bool(true) bool(true) bool(true) Get 'r' bool(true) Get 'rr' bool(true) Get 1 bool(true) bool(true) bool(true) bool(true) Del 'a' bool(true) bool(true) Del all bool(true) QSO bool(true) bool(true) Iterator bool(true) Serialize bool(true) DONE --TEST-- querystring offset set --SKIPIF-- --FILE-- ===DONE=== --EXPECT-- Test foo=bar&bar=baz foo=baz&bar=baz ===DONE=== --TEST-- serialization --SKIPIF-- --FILE-- getClasses() as $class) { if ($class->isInstantiable()) { #printf("%s\n", $class->getName()); $instance = $class->newInstance(); $serialized = serialize($instance); $unserialized = unserialize($serialized); foreach (array("toString", "toArray") as $m) { if ($class->hasMethod($m)) { #printf("%s#%s\n", $class->getName(), $m); $unserialized->$m(); } } } } ?> DONE --EXPECTF-- DONE --TEST-- url from env --SKIPIF-- --ENV-- SERVER_PORT=55555 HTTP_HOST=example.com --GET-- s=b&i=0&e=&a[]=1&a[]=2 --FILE-- "https", "port" => 443))); printf("%s\n", new http\Url(array("path" => "/./up/../down/../././//index.php/.", "query" => null), null, http\Url::SANITIZE_PATH|http\Url::FROM_ENV)); printf("%s\n", new http\Url(null, null, 0)); ?> DONE --EXPECTF-- http://example.com:55555/?s=b&i=0&e=&a[]=1&a[]=2 http://example.com:55555/index?s=b&i=0&e=&a[]=1&a[]=2 https://example.com/?s=b&i=0&e=&a[]=1&a[]=2 http://example.com:55555/index.php/ DONE --TEST-- url properties --SKIPIF-- --FILE-- "changed", "query" => "foo=&added=this"), http\Url::JOIN_PATH | http\Url::JOIN_QUERY | http\Url::STRIP_AUTH | http\Url::STRIP_FRAGMENT ); var_dump($url->scheme); var_dump($url->user); var_dump($url->pass); var_dump($url->host); var_dump($url->port); var_dump($url->path); var_dump($url->query); var_dump($url->fragment); ?> DONE --EXPECT-- Test bool(true) string(4) "http" NULL NULL string(15) "www.example.com" int(8080) string(13) "/path/changed" string(38) "more%5B0%5D=1&more%5B1%5D=2&added=this" NULL DONE --TEST-- url modification --SKIPIF-- --FILE-- mod(array("query" => "set=1"), http\Url::REPLACE); var_dump($tmp->toArray() != $mod->toArray()); var_dump("set=1" === $mod->query); var_dump("new_fragment" === $tmp->mod("#new_fragment")->fragment); ?> DONE --EXPECT-- Test bool(true) bool(true) bool(true) DONE --TEST-- url as string --SKIPIF-- --FILE-- DONE --EXPECT-- Test bool(true) DONE --TEST-- url as array --SKIPIF-- --FILE-- toArray()); var_dump($url->toArray() === $url2->toArray()); ?> DONE --EXPECT-- Test bool(true) DONE --TEST-- url parser --SKIPIF-- --FILE-- DONE --EXPECTF-- Test s: object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } ss: object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s:a object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(1) "a" ["query"]=> NULL ["fragment"]=> NULL } ss:aa object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(2) "aa" ["query"]=> NULL ["fragment"]=> NULL } s:// object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } ss:// object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://a object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "a" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } ss://aa object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(2) "aa" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser with paths --SKIPIF-- --FILE-- DONE --EXPECTF-- Test s:a/ object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(2) "a/" ["query"]=> NULL ["fragment"]=> NULL } ss:aa/ object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(3) "aa/" ["query"]=> NULL ["fragment"]=> NULL } s:/a/ object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(3) "/a/" ["query"]=> NULL ["fragment"]=> NULL } ss:/aa/ object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(4) "/aa/" ["query"]=> NULL ["fragment"]=> NULL } s://a/ object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "a" ["port"]=> NULL ["path"]=> string(1) "/" ["query"]=> NULL ["fragment"]=> NULL } s://h/a object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> string(2) "/a" ["query"]=> NULL ["fragment"]=> NULL } ss://hh/aa object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(2) "hh" ["port"]=> NULL ["path"]=> string(3) "/aa" ["query"]=> NULL ["fragment"]=> NULL } s:///a/b object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(4) "/a/b" ["query"]=> NULL ["fragment"]=> NULL } ss:///aa/bb object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(6) "/aa/bb" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser with query --SKIPIF-- --FILE-- DONE --EXPECTF-- Test s:?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> string(1) "q" ["fragment"]=> NULL } ss:?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> string(2) "qq" ["fragment"]=> NULL } s:/?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(1) "/" ["query"]=> string(1) "q" ["fragment"]=> NULL } ss:/?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> string(1) "/" ["query"]=> string(2) "qq" ["fragment"]=> NULL } s://?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> string(1) "q" ["fragment"]=> NULL } ss://?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> string(2) "qq" ["fragment"]=> NULL } s://h?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> NULL ["query"]=> string(1) "q" ["fragment"]=> NULL } ss://hh?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(2) "hh" ["port"]=> NULL ["path"]=> NULL ["query"]=> string(2) "qq" ["fragment"]=> NULL } s://h/p?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> string(2) "/p" ["query"]=> string(1) "q" ["fragment"]=> NULL } ss://hh/pp?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(2) "hh" ["port"]=> NULL ["path"]=> string(3) "/pp" ["query"]=> string(2) "qq" ["fragment"]=> NULL } s://h:123/p/?q object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> int(123) ["path"]=> string(3) "/p/" ["query"]=> string(1) "q" ["fragment"]=> NULL } ss://hh:123/pp/?qq object(http\Url)#%d (8) { ["scheme"]=> string(2) "ss" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(2) "hh" ["port"]=> int(123) ["path"]=> string(4) "/pp/" ["query"]=> string(2) "qq" ["fragment"]=> NULL } DONE --TEST-- url parser multibyte/locale --SKIPIF-- --FILE-- DONE --EXPECTF-- Test sçheme: object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(5) "hƟst" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst:23/pĂ€th/öf/fıle object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(5) "hƟst" ["port"]=> int(23) ["path"]=> string(16) "/pĂ€th/öf/fıle" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser multibyte/utf-8 --SKIPIF-- --FILE-- DONE --EXPECTF-- Test sçheme: object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(5) "hƟst" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst:23/pĂ€th/öf/fıle object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(5) "hƟst" ["port"]=> int(23) ["path"]=> string(16) "/pĂ€th/öf/fıle" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser multibyte/locale/idna --SKIPIF-- --FILE-- DONE --EXPECTF-- Test sçheme: object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(11) "xn--hst-kwb" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst:23/pĂ€th/öf/fıle object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(11) "xn--hst-kwb" ["port"]=> int(23) ["path"]=> string(16) "/pĂ€th/öf/fıle" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser multibyte/utf-8/idna --SKIPIF-- --FILE-- DONE --EXPECTF-- Test sçheme: object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(11) "xn--hst-kwb" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } sçheme://hƟst:23/pĂ€th/öf/fıle object(http\Url)#%d (8) { ["scheme"]=> string(7) "sçheme" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(11) "xn--hst-kwb" ["port"]=> int(23) ["path"]=> string(16) "/pĂ€th/öf/fıle" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser ipv6 --SKIPIF-- --FILE-- getMessage(),"\n"; } } ?> DONE --EXPECTF-- Test s://[a:80 http\Url::__construct(): Failed to parse hostinfo; expected ']' s://[0] http\Url::__construct(): Failed to parse hostinfo; unexpected '[' s://[::1]:80 object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> NULL ["pass"]=> NULL ["host"]=> string(5) "[::1]" ["port"]=> int(80) ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://mike@[0:0:0:0:0:FFFF:204.152.189.116]/foo object(http\Url)#%d (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "mike" ["pass"]=> NULL ["host"]=> string(24) "[::ffff:204.152.189.116]" ["port"]=> NULL ["path"]=> string(4) "/foo" ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser userinfo --SKIPIF-- --FILE-- getMessage(),"\n"; } } ?> DONE --EXPECTF-- Test s://:@ object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(0) "" ["pass"]=> string(0) "" ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://u@ object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(1) "u" ["pass"]=> NULL ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://u:@ object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(1) "u" ["pass"]=> string(0) "" ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://u:p@ object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(1) "u" ["pass"]=> string(1) "p" ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user:pass@ object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> string(4) "pass" ["host"]=> NULL ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user:pass@host object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> string(4) "pass" ["host"]=> string(4) "host" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://u@h object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(1) "u" ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user@h object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> NULL ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://u@host object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(1) "u" ["pass"]=> NULL ["host"]=> string(4) "host" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user:p@h object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> string(1) "p" ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user:pass@h object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> string(4) "pass" ["host"]=> string(1) "h" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } s://user:pass@host object(http\Url)#1 (8) { ["scheme"]=> string(1) "s" ["user"]=> string(4) "user" ["pass"]=> string(4) "pass" ["host"]=> string(4) "host" ["port"]=> NULL ["path"]=> NULL ["query"]=> NULL ["fragment"]=> NULL } DONE --TEST-- url parser multibyte/locale/topct --SKIPIF-- --FILE-- DONE --EXPECTF-- Test object(http\Url)#%d (8) { ["scheme"]=> string(4) "http" ["user"]=> string(4) "mike" ["pass"]=> string(12) "pa%C3%9Fwort" ["host"]=> string(15) "𐌀𐌁𐌂.it" ["port"]=> NULL ["path"]=> string(15) "/for/%E2%82%AC/" ["query"]=> string(9) "by=%C2%A2" ["fragment"]=> string(6) "%C3%B8" } DONE --TEST-- url parser multibyte/utf-8/topct --SKIPIF-- --FILE-- DONE --EXPECTF-- Test object(http\Url)#%d (8) { ["scheme"]=> string(4) "http" ["user"]=> string(4) "mike" ["pass"]=> string(12) "pa%C3%9Fwort" ["host"]=> string(15) "𐌀𐌁𐌂.it" ["port"]=> NULL ["path"]=> string(15) "/for/%E2%82%AC/" ["query"]=> string(9) "by=%C2%A2" ["fragment"]=> string(6) "%C3%B8" } DONE --TEST-- version parse error --SKIPIF-- --FILE-- setHttpVersion("1-1"); $m->setHttpVersion("one.one"); } catch (http\Exception $e) { echo $e->getMessage(),"\n"; } ?> --EXPECTF-- Notice: http\Message::setHttpVersion(): Non-standard version separator '-' in HTTP protocol version '1-1' in %s http\Message::setHttpVersion(): Could not parse HTTP protocol version 'one.one' ˜vÏ(Ï ád_(òS‘Ź/lZFGBMB