diff options
author | Yasuo Ohgaki <yohgaki@php.net> | 2016-08-11 08:03:45 +0900 |
---|---|---|
committer | Yasuo Ohgaki <yohgaki@php.net> | 2016-08-11 08:31:48 +0900 |
commit | a53a6b3fb4060c9c71a84b0acaaa0211777f6e17 (patch) | |
tree | ef654763fd8921ccbe5436996780adbd9c439577 | |
parent | 2644943968de8af608b25daf83d733a1bdb98269 (diff) | |
download | php-git-a53a6b3fb4060c9c71a84b0acaaa0211777f6e17.tar.gz |
Fix URL rewriter issues
21 files changed, 2434 insertions, 532 deletions
@@ -62,6 +62,17 @@ PHP 7.1 UPGRADE NOTES different sequence of outputs to previous versions. If you relied on mt_srand() to produce a deterministic sequence, it can be called using mt_srand($seed, MT_RAND_PHP) to produce old the sequences. + . URL rewriter has been improved. + . Use dedicated buffer for Session module rewrite and User rewrite. + . Full path URL rewrite is supported. Allowed domain can be specified. + $_SERVER['HTTP_HOST'] is allowed by default when host whitelist is empty. + . Use session.trans_sid_tags and session.trans_sid_hosts to control + session rewrite. + . Use url_rewriter.tags and url_rewriter.hosts to control user rewrite. + . <form>'s "action" attribute is used to check if URL rewrite is allowed + and listed under hosts whitelist. + . <fieldset> is no longer considered as a special tag. <form> is the + only tag considered special. - JSON: . The serialize_precision is used instead of precision when encoding double @@ -163,6 +174,8 @@ PHP 7.1 UPGRADE NOTES - mb_ereg() and mb_ereg_replace() reject illegal byte sequences. - FILTER_FLAG_EMAIL_UNICODE can be used with filter_var() for email validation according to RFC 6531. +- output_reset_rewrite_vars() no longer reset session URL rewrite vars. + ======================================== 6. New Functions diff --git a/ext/session/session.c b/ext/session/session.c index bbf5b0fb94..9c143e2fcf 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -1521,6 +1521,7 @@ PHPAPI void php_session_reset_id(void) /* {{{ */ { int module_number = PS(module_number); zval *sid, *data, *ppid; + zend_bool apply_trans_sid; if (!PS(id)) { php_error_docref(NULL, E_WARNING, "Cannot set session ID - session ID is not initialized"); @@ -1561,20 +1562,26 @@ PHPAPI void php_session_reset_id(void) /* {{{ */ } /* Apply trans sid if sid cookie is not set */ - if (APPLY_TRANS_SID - && (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) { - ZVAL_DEREF(data); - if (Z_TYPE_P(data) == IS_ARRAY && (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) { - ZVAL_DEREF(ppid); - } else { - /* FIXME: Resetting vars are required when - session is stop/start/regenerated. However, - php_url_scanner_reset_vars() resets all vars - including other URL rewrites set by elsewhere. */ - /* php_url_scanner_reset_vars(); */ - php_url_scanner_add_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1); + apply_trans_sid = 0; + if (APPLY_TRANS_SID) { + apply_trans_sid = 1; + if (PS(use_cookies) && + (data = zend_hash_str_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE") - 1))) { + ZVAL_DEREF(data); + if (Z_TYPE_P(data) == IS_ARRAY && + (ppid = zend_hash_str_find(Z_ARRVAL_P(data), PS(session_name), strlen(PS(session_name))))) { + ZVAL_DEREF(ppid); + apply_trans_sid = 0; + } } } + if (apply_trans_sid) { + zend_string *sname; + sname = zend_string_init(PS(session_name), strlen(PS(session_name)), 0); + php_url_scanner_reset_session_var(sname, 1); /* This may fail when session name has changed */ + zend_string_release(sname); + php_url_scanner_add_session_var(PS(session_name), strlen(PS(session_name)), ZSTR_VAL(PS(id)), ZSTR_LEN(PS(id)), 1); + } } /* }}} */ diff --git a/ext/session/tests/021.phpt b/ext/session/tests/021.phpt index e199972899..4a97d7d32a 100644 --- a/ext/session/tests/021.phpt +++ b/ext/session/tests/021.phpt @@ -16,11 +16,15 @@ session.save_handler=files <?php error_reporting(E_ALL); +ini_set('session.trans_sid_hosts', 'php.net'); +$_SERVER['HTTP_HOST'] = 'php.net'; session_id("abtest"); session_start(); ?> -<form> +<form action="//bad.net/do.php"> +<fieldset> +<form action="//php.net/do.php"> <fieldset> <?php @@ -29,7 +33,7 @@ ob_flush(); ini_set("url_rewriter.tags", "a=href,area=href,frame=src,input=src,form="); ?> -<form> +<form action="../do.php"> <fieldset> <?php @@ -38,7 +42,7 @@ ob_flush(); ini_set("url_rewriter.tags", "a=href,area=href,frame=src,input=src,form=fakeentry"); ?> -<form> +<form action="/do.php"> <fieldset> <?php @@ -47,18 +51,20 @@ ob_flush(); ini_set("url_rewriter.tags", "a=href,fieldset=,area=href,frame=src,input=src"); ?> -<form> +<form action="/foo/do.php"> <fieldset> <?php session_destroy(); ?> --EXPECT-- -<form><input type="hidden" name="PHPSESSID" value="abtest" /> -<fieldset><input type="hidden" name="PHPSESSID" value="abtest" /> -<form><input type="hidden" name="PHPSESSID" value="abtest" /> +<form action="//bad.net/do.php"> +<fieldset> +<form action="//php.net/do.php"><input type="hidden" name="PHPSESSID" value="abtest" /> +<fieldset> +<form action="../do.php"><input type="hidden" name="PHPSESSID" value="abtest" /> +<fieldset> +<form action="/do.php"><input type="hidden" name="PHPSESSID" value="abtest" /> <fieldset> -<form><input type="hidden" name="PHPSESSID" value="abtest" /> +<form action="/foo/do.php"><input type="hidden" name="PHPSESSID" value="abtest" /> <fieldset> -<form> -<fieldset><input type="hidden" name="PHPSESSID" value="abtest" /> diff --git a/ext/session/tests/bug26862.phpt b/ext/session/tests/bug26862.phpt index 7990f74359..9b15305b77 100644 --- a/ext/session/tests/bug26862.phpt +++ b/ext/session/tests/bug26862.phpt @@ -6,6 +6,8 @@ Bug #26862 (ob_flush() before output_reset_rewrite_vars() results in data loss) html_errors=0 session.use_trans_sid=0 session.save_handler=files +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" --FILE-- <?php session_start(); diff --git a/ext/session/tests/bug50308.phpt b/ext/session/tests/bug50308.phpt index 110277ce3c..fe0b0e89fa 100644 --- a/ext/session/tests/bug50308.phpt +++ b/ext/session/tests/bug50308.phpt @@ -16,15 +16,21 @@ session.use_only_cookies=0 <a href="foo"/> <a href="foo" /> <a href=foo/> +<a href="/"> <a href=/> <a href=?foo=bar/> <a href="?foo=bar"/> +<a href=./> +<a href="./"> --EXPECTF-- <a href="?PHPSESSID=%s"/> <a href="?PHPSESSID=%s" /> <a href="foo?PHPSESSID=%s"/> <a href="foo?PHPSESSID=%s" /> <a href=foo/?PHPSESSID=%s> +<a href="/?PHPSESSID=%s"> <a href=/?PHPSESSID=%s> <a href=?foo=bar/&PHPSESSID=%s> <a href="?foo=bar&PHPSESSID=%s"/> +<a href=./?PHPSESSID=%s> +<a href="./?PHPSESSID=%s"> diff --git a/ext/session/tests/session_basic3.phpt b/ext/session/tests/session_basic3.phpt index 3cc90a8eef..0337151cf0 100644 --- a/ext/session/tests/session_basic3.phpt +++ b/ext/session/tests/session_basic3.phpt @@ -12,8 +12,7 @@ session.gc_divisor=1000 session.gc_maxlifetime=300 session.save_path= session.name=PHPSESSID ---XFAIL-- -Waiting url_scanner_ex.re fix. https://bugs.php.net/bug.php?id=68970 +url_rewriter.hosts= --SKIPIF-- <?php include('skipif.inc'); ?> --FILE-- @@ -206,15 +205,15 @@ echo ' <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="http://php.net/script.php" method="post"> +<form method="post" action="http://php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="https://php.net/script.php" method="post"> +<form method="post" action="https://php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="//php.net/script.php" method="post"> +<form method="post" action="//php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> @@ -233,15 +232,6 @@ ob_end_flush(); *** Test trans sid *** <a href="/?PHPSESSID=testid">test</a> -<a href="/?PHPSESSID=testid#bar">test</a> -<a href="/?foo&PHPSESSID=testid">test</a> -<a href="/?foo&PHPSESSID=testid#bar">test</a> -<a href="/?foo=var&PHPSESSID=testid">test</a> -<a href="/?foo=var&PHPSESSID=testid#bar">test</a> -<a href="file.php?PHPSESSID=testid">test</a> -<a href="file.php?foo&PHPSESSID=testid">test</a> -<a href="file.php?foo=var&PHPSESSID=testid">test</a> -<a href="/?PHPSESSID=testid">test</a> <a href="/path?PHPSESSID=testid">test</a> <a href="/path/?PHPSESSID=testid">test</a> <a href="/path/?foo=var&PHPSESSID=testid">test</a> @@ -257,14 +247,23 @@ ob_end_flush(); <a href="../path/?PHPSESSID=testid#bar">test</a> <a href="../path/?foo=var&PHPSESSID=testid#bar">test</a> -<a href="/?foo">test</a> -<a href="/?foo#bar">test</a> -<a href="/?foo=var">test</a> -<a href="/?foo=var#bar">test</a> -<a href="../?foo">test</a> -<a href="../?foo#bar">test</a> -<a href="../?foo=var">test</a> -<a href="../?foo=var#bar">test</a> +<a href="/?foo&PHPSESSID=testid">test</a> +<a href="/?foo&PHPSESSID=testid#bar">test</a> +<a href="/?foo=var&PHPSESSID=testid">test</a> +<a href="/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="../?foo&PHPSESSID=testid">test</a> +<a href="../?foo&PHPSESSID=testid#bar">test</a> +<a href="../?foo=var&PHPSESSID=testid">test</a> +<a href="../?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="file.php?PHPSESSID=testid">test</a> +<a href="file.php?foo&PHPSESSID=testid">test</a> +<a href="file.php?foo=var&PHPSESSID=testid">test</a> +<a href="file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="../file.php?PHPSESSID=testid">test</a> +<a href="../file.php?foo&PHPSESSID=testid">test</a> +<a href="../file.php?foo=var&PHPSESSID=testid">test</a> +<a href="../file.php?foo=var&PHPSESSID=testid#bar">test</a> <a href="http://php.net">test</a> <a href="http://php.net/">test</a> @@ -317,31 +316,31 @@ ob_end_flush(); <a href="//php.net/some/path/file.php?foo=var">test</a> <a href="//php.net/some/path/file.php?foo=var#bar">test</a> -<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="http://php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form method="post" action="http://php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="https://php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form method="post" action="https://php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> -<form action="//php.net/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /><input type="hidden" name="PHPSESSID" value="testid" /> +<form method="post" action="//php.net/script.php"> <input type="text" name="test1"></input> <input type="text" name="test2" /> </form> @@ -349,4 +348,4 @@ NULL *** Cleanup *** bool(true) string(6) "testid" -bool(true)
\ No newline at end of file +bool(true) diff --git a/ext/session/tests/session_basic4.phpt b/ext/session/tests/session_basic4.phpt new file mode 100644 index 0000000000..0a0f9749f8 --- /dev/null +++ b/ext/session/tests/session_basic4.phpt @@ -0,0 +1,67 @@ +--TEST-- +Test basic function : variation4 use_trans_sid +--INI-- +session.use_strict_mode=0 +session.use_only_cookies=0 +session.use_trans_sid=1 +session.save_handler=files +session.hash_bits_per_character=4 +session.hash_function=0 +session.gc_probability=1 +session.gc_divisor=1000 +session.gc_maxlifetime=300 +session.save_path= +session.name=PHPSESSID +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" +--SKIPIF-- +<?php include('skipif.inc'); ?> +--FILE-- +<?php + +ob_start(); + +/* + * Prototype : session.use_trans_sid=1 + * Description : Test basic functionality. + * Source code : ext/session/session.c + */ + +echo "*** Testing basic session functionality : variation4 use_trans_sid ***\n"; + +echo "*** Test trans sid ***\n"; +output_add_rewrite_var('testvar1','testvalue1'); + +session_id('test1'); +session_start(); + +echo ' +<a href="/"> +<form action="" method="post"> +</form> +'; + +session_commit(); + +output_add_rewrite_var('testvar2','testvalue2'); + +session_id('test2'); +session_start(); +echo ' +<a href="/"> +<form action="" method="post"> +</form> +'; + + +--EXPECT-- +*** Testing basic session functionality : variation4 use_trans_sid *** +*** Test trans sid *** + +<a href="/?PHPSESSID=test2&testvar1=testvalue1&testvar2=testvalue2"> +<form action="" method="post"><input type="hidden" name="testvar1" value="testvalue1" /><input type="hidden" name="testvar2" value="testvalue2" /><input type="hidden" name="PHPSESSID" value="test2" /> +</form> + +<a href="/?PHPSESSID=test2&testvar1=testvalue1&testvar2=testvalue2"> +<form action="" method="post"><input type="hidden" name="testvar1" value="testvalue1" /><input type="hidden" name="testvar2" value="testvalue2" /><input type="hidden" name="PHPSESSID" value="test2" /> +</form> diff --git a/ext/session/tests/session_basic5.phpt b/ext/session/tests/session_basic5.phpt new file mode 100644 index 0000000000..7e3bb7fc21 --- /dev/null +++ b/ext/session/tests/session_basic5.phpt @@ -0,0 +1,446 @@ +--TEST-- +Test basic function : variation5 use_trans_sid +--INI-- +session.use_strict_mode=0 +session.use_only_cookies=0 +session.use_trans_sid=1 +session.save_handler=files +session.hash_bits_per_character=4 +session.hash_function=0 +session.gc_probability=1 +session.gc_divisor=1000 +session.gc_maxlifetime=300 +session.save_path= +session.name=PHPSESSID +--SKIPIF-- +<?php include('skipif.inc'); ?> +--FILE-- +<?php +ob_start(); + +$_SERVER['HTTP_HOST'] = 'php.net'; +ini_set('session.trans_sid_hosts','php.net,example.com'); + +/* + * Prototype : session.use_trans_sid=1 + * Description : Test basic functionality. + * Source code : ext/session/session.c + */ + +echo "*** Testing basic session functionality : variation5 use_trans_sid ***\n"; +echo "*** Test trans sid ***\n"; + +$session_id = 'testid'; +session_id($session_id); +session_start(); +// Should add session ID to allowed hosts only for SECURITY +echo ' +<a href="/">test</a> +<a href="/path">test</a> +<a href="/path/">test</a> +<a href="/path/?foo=var">test</a> +<a href="../">test</a> +<a href="../path">test</a> +<a href="../path/">test</a> +<a href="../path/?foo=var">test</a> + +<a href="/#bar">test</a> +<a href="/path/#bar">test</a> +<a href="/path/?foo=var#bar">test</a> +<a href="../#bar">test</a> +<a href="../path/#bar">test</a> +<a href="../path/?foo=var#bar">test</a> + +<a href="/?foo">test</a> +<a href="/?foo#bar">test</a> +<a href="/?foo=var">test</a> +<a href="/?foo=var#bar">test</a> +<a href="../?foo">test</a> +<a href="../?foo#bar">test</a> +<a href="../?foo=var">test</a> +<a href="../?foo=var#bar">test</a> + +<a href="file.php">test</a> +<a href="file.php?foo">test</a> +<a href="file.php?foo=var">test</a> +<a href="file.php?foo=var#bar">test</a> +<a href="../file.php">test</a> +<a href="../file.php?foo">test</a> +<a href="../file.php?foo=var">test</a> +<a href="../file.php?foo=var#bar">test</a> + +<a href="http://php.net">test</a> +<a href="http://php.net/">test</a> +<a href="http://php.net/#bar">test</a> +<a href="http://php.net/?foo">test</a> +<a href="http://php.net/?foo#bar">test</a> +<a href="http://php.net/?foo=var">test</a> +<a href="http://php.net/?foo=var#bar">test</a> +<a href="http://php.net/file.php">test</a> +<a href="http://php.net/file.php#bar">test</a> +<a href="http://php.net/file.php?foo">test</a> +<a href="http://php.net/file.php?foo#bar">test</a> +<a href="http://php.net/file.php?foo=var">test</a> +<a href="http://php.net/file.php?foo=var#bar">test</a> +<a href="http://php.net/some/path/file.php">test</a> +<a href="http://php.net/some/path/file.php?foo">test</a> +<a href="http://php.net/some/path/file.php?foo=var">test</a> +<a href="http://php.net/some/path/file.php?foo=var#bar">test</a> + +<a href="https://php.net">test</a> +<a href="https://php.net/">test</a> +<a href="https://php.net/?foo=var#bar">test</a> +<a href="https://php.net/file.php">test</a> +<a href="https://php.net/file.php?foo=var#bar">test</a> +<a href="https://php.net/some/path/file.php">test</a> +<a href="https://php.net/some/path/file.php?foo=var#bar">test</a> +<a href="https://php.net:8443">test</a> +<a href="https://php.net:8443/">test</a> +<a href="https://php.net:8443/?foo=var#bar">test</a> +<a href="https://php.net:8443/file.php">test</a> +<a href="https://php.net:8443/file.php?foo=var#bar">test</a> +<a href="https://php.net:8443/some/path/file.php">test</a> +<a href="https://php.net:8443/some/path/file.php?foo=var#bar">test</a> + +<a href="//php.net">test</a> +<a href="//php.net/">test</a> +<a href="//php.net/#bar">test</a> +<a href="//php.net/?foo">test</a> +<a href="//php.net/?foo#bar">test</a> +<a href="//php.net/?foo=var">test</a> +<a href="//php.net/?foo=var#bar">test</a> +<a href="//php.net/file.php">test</a> +<a href="//php.net/file.php#bar">test</a> +<a href="//php.net/file.php?foo">test</a> +<a href="//php.net/file.php?foo#bar">test</a> +<a href="//php.net/file.php?foo=var">test</a> +<a href="//php.net/file.php?foo=var#bar">test</a> +<a href="//php.net/some/path/file.php">test</a> +<a href="//php.net/some/path/file.php?foo">test</a> +<a href="//php.net/some/path/file.php?foo=var">test</a> +<a href="//php.net/some/path/file.php?foo=var#bar">test</a> + +<form action="script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="../script.php" method="post">r + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="/path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="../path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="http://php.net/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="https://php.net/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="//php.net/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> + + +<a href="http://bad.com">test</a> +<a href="http://bad.com/">test</a> +<a href="http://bad.com/#bar">test</a> +<a href="http://bad.com/?foo">test</a> +<a href="http://bad.com/?foo#bar">test</a> +<a href="http://bad.com/?foo=var">test</a> +<a href="http://bad.com/?foo=var#bar">test</a> +<a href="http://bad.com/file.php">test</a> +<a href="http://bad.com/file.php#bar">test</a> +<a href="http://bad.com/file.php?foo">test</a> +<a href="http://bad.com/file.php?foo#bar">test</a> +<a href="http://bad.com/file.php?foo=var">test</a> +<a href="http://bad.com/file.php?foo=var#bar">test</a> +<a href="http://bad.com/some/path/file.php">test</a> +<a href="http://bad.com/some/path/file.php?foo">test</a> +<a href="http://bad.com/some/path/file.php?foo=var">test</a> +<a href="http://bad.com/some/path/file.php?foo=var#bar">test</a> + +<a href="https://bad.com">test</a> +<a href="https://bad.com/">test</a> +<a href="https://bad.com/?foo=var#bar">test</a> +<a href="https://bad.com/file.php">test</a> +<a href="https://bad.com/file.php?foo=var#bar">test</a> +<a href="https://bad.com/some/path/file.php">test</a> +<a href="https://bad.com/some/path/file.php?foo=var#bar">test</a> +<a href="https://bad.com:8443">test</a> +<a href="https://bad.com:8443/">test</a> +<a href="https://bad.com:8443/?foo=var#bar">test</a> +<a href="https://bad.com:8443/file.php">test</a> +<a href="https://bad.com:8443/file.php?foo=var#bar">test</a> +<a href="https://bad.com:8443/some/path/file.php">test</a> +<a href="https://bad.com:8443/some/path/file.php?foo=var#bar">test</a> + +<a href="//bad.com">test</a> +<a href="//bad.com/">test</a> +<a href="//bad.com/#bar">test</a> +<a href="//bad.com/?foo">test</a> +<a href="//bad.com/?foo#bar">test</a> +<a href="//bad.com/?foo=var">test</a> +<a href="//bad.com/?foo=var#bar">test</a> +<a href="//bad.com/file.php">test</a> +<a href="//bad.com/file.php#bar">test</a> +<a href="//bad.com/file.php?foo">test</a> +<a href="//bad.com/file.php?foo#bar">test</a> +<a href="//bad.com/file.php?foo=var">test</a> +<a href="//bad.com/file.php?foo=var#bar">test</a> +<a href="//bad.com/some/path/file.php">test</a> +<a href="//bad.com/some/path/file.php?foo">test</a> +<a href="//bad.com/some/path/file.php?foo=var">test</a> +<a href="//bad.com/some/path/file.php?foo=var#bar">test</a> + +<form action="//bad.com/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com/foo/../script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com//path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com/foo/bar../path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="http://bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="https://bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="//bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> + +'; +var_dump(session_commit()); + +echo "*** Cleanup ***\n"; +var_dump(session_start()); +var_dump(session_id()); +var_dump(session_destroy()); + +ob_end_flush(); +?> +--EXPECT-- +*** Testing basic session functionality : variation5 use_trans_sid *** +*** Test trans sid *** + +<a href="/?PHPSESSID=testid">test</a> +<a href="/path?PHPSESSID=testid">test</a> +<a href="/path/?PHPSESSID=testid">test</a> +<a href="/path/?foo=var&PHPSESSID=testid">test</a> +<a href="../?PHPSESSID=testid">test</a> +<a href="../path?PHPSESSID=testid">test</a> +<a href="../path/?PHPSESSID=testid">test</a> +<a href="../path/?foo=var&PHPSESSID=testid">test</a> + +<a href="/?PHPSESSID=testid#bar">test</a> +<a href="/path/?PHPSESSID=testid#bar">test</a> +<a href="/path/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="../?PHPSESSID=testid#bar">test</a> +<a href="../path/?PHPSESSID=testid#bar">test</a> +<a href="../path/?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="/?foo&PHPSESSID=testid">test</a> +<a href="/?foo&PHPSESSID=testid#bar">test</a> +<a href="/?foo=var&PHPSESSID=testid">test</a> +<a href="/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="../?foo&PHPSESSID=testid">test</a> +<a href="../?foo&PHPSESSID=testid#bar">test</a> +<a href="../?foo=var&PHPSESSID=testid">test</a> +<a href="../?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="file.php?PHPSESSID=testid">test</a> +<a href="file.php?foo&PHPSESSID=testid">test</a> +<a href="file.php?foo=var&PHPSESSID=testid">test</a> +<a href="file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="../file.php?PHPSESSID=testid">test</a> +<a href="../file.php?foo&PHPSESSID=testid">test</a> +<a href="../file.php?foo=var&PHPSESSID=testid">test</a> +<a href="../file.php?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="http://php.net/?PHPSESSID=testid">test</a> +<a href="http://php.net/?PHPSESSID=testid">test</a> +<a href="http://php.net/?PHPSESSID=testid#bar">test</a> +<a href="http://php.net/?foo&PHPSESSID=testid">test</a> +<a href="http://php.net/?foo&PHPSESSID=testid#bar">test</a> +<a href="http://php.net/?foo=var&PHPSESSID=testid">test</a> +<a href="http://php.net/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="http://php.net/file.php?PHPSESSID=testid">test</a> +<a href="http://php.net/file.php?PHPSESSID=testid#bar">test</a> +<a href="http://php.net/file.php?foo&PHPSESSID=testid">test</a> +<a href="http://php.net/file.php?foo&PHPSESSID=testid#bar">test</a> +<a href="http://php.net/file.php?foo=var&PHPSESSID=testid">test</a> +<a href="http://php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="http://php.net/some/path/file.php?PHPSESSID=testid">test</a> +<a href="http://php.net/some/path/file.php?foo&PHPSESSID=testid">test</a> +<a href="http://php.net/some/path/file.php?foo=var&PHPSESSID=testid">test</a> +<a href="http://php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="https://php.net/?PHPSESSID=testid">test</a> +<a href="https://php.net/?PHPSESSID=testid">test</a> +<a href="https://php.net/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="https://php.net/file.php?PHPSESSID=testid">test</a> +<a href="https://php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="https://php.net/some/path/file.php?PHPSESSID=testid">test</a> +<a href="https://php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="https://php.net:8443/?PHPSESSID=testid">test</a> +<a href="https://php.net:8443/?PHPSESSID=testid">test</a> +<a href="https://php.net:8443/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="https://php.net:8443/file.php?PHPSESSID=testid">test</a> +<a href="https://php.net:8443/file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="https://php.net:8443/some/path/file.php?PHPSESSID=testid">test</a> +<a href="https://php.net:8443/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a> + +<a href="//php.net/?PHPSESSID=testid">test</a> +<a href="//php.net/?PHPSESSID=testid">test</a> +<a href="//php.net/?PHPSESSID=testid#bar">test</a> +<a href="//php.net/?foo&PHPSESSID=testid">test</a> +<a href="//php.net/?foo&PHPSESSID=testid#bar">test</a> +<a href="//php.net/?foo=var&PHPSESSID=testid">test</a> +<a href="//php.net/?foo=var&PHPSESSID=testid#bar">test</a> +<a href="//php.net/file.php?PHPSESSID=testid">test</a> +<a href="//php.net/file.php?PHPSESSID=testid#bar">test</a> +<a href="//php.net/file.php?foo&PHPSESSID=testid">test</a> +<a href="//php.net/file.php?foo&PHPSESSID=testid#bar">test</a> +<a href="//php.net/file.php?foo=var&PHPSESSID=testid">test</a> +<a href="//php.net/file.php?foo=var&PHPSESSID=testid#bar">test</a> +<a href="//php.net/some/path/file.php?PHPSESSID=testid">test</a> +<a href="//php.net/some/path/file.php?foo&PHPSESSID=testid">test</a> +<a href="//php.net/some/path/file.php?foo=var&PHPSESSID=testid">test</a> +<a href="//php.net/some/path/file.php?foo=var&PHPSESSID=testid#bar">test</a> + +<form action="script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="../script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" />r + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="/path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="../path/script.php" method="post"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="http://php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="https://php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="//php.net/script.php"><input type="hidden" name="PHPSESSID" value="testid" /> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> + + +<a href="http://bad.com">test</a> +<a href="http://bad.com/">test</a> +<a href="http://bad.com/#bar">test</a> +<a href="http://bad.com/?foo">test</a> +<a href="http://bad.com/?foo#bar">test</a> +<a href="http://bad.com/?foo=var">test</a> +<a href="http://bad.com/?foo=var#bar">test</a> +<a href="http://bad.com/file.php">test</a> +<a href="http://bad.com/file.php#bar">test</a> +<a href="http://bad.com/file.php?foo">test</a> +<a href="http://bad.com/file.php?foo#bar">test</a> +<a href="http://bad.com/file.php?foo=var">test</a> +<a href="http://bad.com/file.php?foo=var#bar">test</a> +<a href="http://bad.com/some/path/file.php">test</a> +<a href="http://bad.com/some/path/file.php?foo">test</a> +<a href="http://bad.com/some/path/file.php?foo=var">test</a> +<a href="http://bad.com/some/path/file.php?foo=var#bar">test</a> + +<a href="https://bad.com">test</a> +<a href="https://bad.com/">test</a> +<a href="https://bad.com/?foo=var#bar">test</a> +<a href="https://bad.com/file.php">test</a> +<a href="https://bad.com/file.php?foo=var#bar">test</a> +<a href="https://bad.com/some/path/file.php">test</a> +<a href="https://bad.com/some/path/file.php?foo=var#bar">test</a> +<a href="https://bad.com:8443">test</a> +<a href="https://bad.com:8443/">test</a> +<a href="https://bad.com:8443/?foo=var#bar">test</a> +<a href="https://bad.com:8443/file.php">test</a> +<a href="https://bad.com:8443/file.php?foo=var#bar">test</a> +<a href="https://bad.com:8443/some/path/file.php">test</a> +<a href="https://bad.com:8443/some/path/file.php?foo=var#bar">test</a> + +<a href="//bad.com">test</a> +<a href="//bad.com/">test</a> +<a href="//bad.com/#bar">test</a> +<a href="//bad.com/?foo">test</a> +<a href="//bad.com/?foo#bar">test</a> +<a href="//bad.com/?foo=var">test</a> +<a href="//bad.com/?foo=var#bar">test</a> +<a href="//bad.com/file.php">test</a> +<a href="//bad.com/file.php#bar">test</a> +<a href="//bad.com/file.php?foo">test</a> +<a href="//bad.com/file.php?foo#bar">test</a> +<a href="//bad.com/file.php?foo=var">test</a> +<a href="//bad.com/file.php?foo=var#bar">test</a> +<a href="//bad.com/some/path/file.php">test</a> +<a href="//bad.com/some/path/file.php?foo">test</a> +<a href="//bad.com/some/path/file.php?foo=var">test</a> +<a href="//bad.com/some/path/file.php?foo=var#bar">test</a> + +<form action="//bad.com/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com/foo/../script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com//path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form action="https://bad.com/foo/bar../path/script.php" method="post"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="http://bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="https://bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> +<form method="post" action="//bad.com/script.php"> + <input type="text" name="test1"></input> + <input type="text" name="test2" /> +</form> + +NULL +*** Cleanup *** +bool(true) +string(6) "testid" +bool(true) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 88e628ae05..ef628cc6cb 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -3481,7 +3481,8 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ memset(&BG(serialize), 0, sizeof(BG(serialize))); memset(&BG(unserialize), 0, sizeof(BG(unserialize))); - memset(&BG(url_adapt_state_ex), 0, sizeof(BG(url_adapt_state_ex))); + memset(&BG(url_adapt_session_ex), 0, sizeof(BG(url_adapt_session_ex))); + memset(&BG(url_adapt_output_ex), 0, sizeof(BG(url_adapt_output_ex))); #if defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T) memset(&BG(mblen_state), 0, sizeof(BG(mblen_state))); @@ -3495,9 +3496,13 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ static void basic_globals_dtor(php_basic_globals *basic_globals_p) /* {{{ */ { - if (basic_globals_p->url_adapt_state_ex.tags) { - zend_hash_destroy(basic_globals_p->url_adapt_state_ex.tags); - free(basic_globals_p->url_adapt_state_ex.tags); + if (basic_globals_p->url_adapt_session_ex.tags) { + zend_hash_destroy(basic_globals_p->url_adapt_session_ex.tags); + free(basic_globals_p->url_adapt_session_ex.tags); + } + if (basic_globals_p->url_adapt_output_ex.tags) { + zend_hash_destroy(basic_globals_p->url_adapt_output_ex.tags); + free(basic_globals_p->url_adapt_output_ex.tags); } } /* }}} */ diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index d41fe74ada..790b7fbaa0 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -215,7 +215,10 @@ typedef struct _php_basic_globals { } unserialize; /* url_scanner_ex.re */ - url_adapt_state_ex_t url_adapt_state_ex; + url_adapt_state_ex_t url_adapt_session_ex; + HashTable url_adapt_session_hosts_ht; + url_adapt_state_ex_t url_adapt_output_ex; + HashTable url_adapt_output_hosts_ht; #ifdef HAVE_MMAP void *mmap_file; diff --git a/ext/standard/tests/general_functions/bug44394.phpt b/ext/standard/tests/general_functions/bug44394.phpt index 26351a28a1..4ecd8a1123 100644 --- a/ext/standard/tests/general_functions/bug44394.phpt +++ b/ext/standard/tests/general_functions/bug44394.phpt @@ -1,5 +1,8 @@ --TEST-- Bug #44394 (Last two bytes missing from output) +--INI-- +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" --FILE-- <?php diff --git a/ext/standard/tests/general_functions/bug44394_2.phpt b/ext/standard/tests/general_functions/bug44394_2.phpt index 3ca53974e3..c2d638be7e 100644 --- a/ext/standard/tests/general_functions/bug44394_2.phpt +++ b/ext/standard/tests/general_functions/bug44394_2.phpt @@ -5,6 +5,8 @@ Bug #44394 (Last two bytes missing from output) with session.use_trans_id --INI-- session.name=PHPSESSID session.use_only_cookies=0 +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" --FILE-- <?php @@ -32,4 +34,4 @@ foreach (glob(__DIR__ . '/sess_*') as $filename) { } ?> --EXPECTF-- -<a href='a?q=1&PHPSESSID=%s&a=b'>asd</a> +<a href='a?q=1&a=b&PHPSESSID=%s'>asd</a> diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt new file mode 100644 index 0000000000..4b21777202 --- /dev/null +++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic1.phpt @@ -0,0 +1,121 @@ +--TEST-- +Test output_add_rewrite_var() function basic feature +--SKIPIF-- +--INI-- +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" +--FILE-- +<?php + ob_start(); +// Common setting +ini_set('url_rewriter.hosts', 'php.net,www.php.net'); +ini_set('session.trans_sid_hosts', 'php.net,www.php.net'); +ini_set('session.use_only_cookies', 0); +ini_set('session.use_cookies', 0); +ini_set('session.use_strict_mode', 0); +session_id('testid'); + +output_add_rewrite_var('<name>', '<value>'); +?> +Without session +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> +<form action="//php.net/bar.php" method="get"> +<form action="http://php.net/bar.php" method="get"> +<form action="bad://php.net/bar.php" method="get"> +<form action="//www.php.net/bar.php" method="get"> + +<?php +ini_set('session.use_trans_sid', 0); +session_start(); +output_add_rewrite_var('<name>', '<value>'); +?> +Test use_trans_sid=0 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </form> +<form action="//php.net/bar.php" method="get"> </form> +<form action="http://php.net/bar.php" method="get"> </form> +<form action="bad://php.net/bar.php" method="get"> </form> +<form action="//www.php.net/bar.php" method="get"> </form> + +<?php +session_commit(); +ini_set('session.use_trans_sid', 1); +output_reset_rewrite_vars(); +session_start(); +output_add_rewrite_var('<NAME>', '<VALUE>'); +?> +Test use_trans_sid=1 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </form> +<form action="//php.net/bar.php" method="get"> </form> +<form action="http://php.net/bar.php" method="get"> </form> +<form action="bad://php.net/bar.php" method="get"> </form> +<form action="//www.php.net/bar.php" method="get"> </form> + +--EXPECT-- +Without session +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> +<form action="bad://php.net/bar.php" method="get"> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> + +Test use_trans_sid=0 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="bad://php.net/bar.php" method="get"> </form> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> + +Test use_trans_sid=1 +<a href="?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </form> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </form> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </form> +<form action="bad://php.net/bar.php" method="get"> </form> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </form> diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt new file mode 100644 index 0000000000..02b2f03c88 --- /dev/null +++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic2.phpt @@ -0,0 +1,121 @@ +--TEST-- +Test output_add_rewrite_var() function basic feature +--SKIPIF-- +--INI-- +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" +--FILE-- +<?php + ob_start(); +// Common setting +ini_set('url_rewriter.hosts', 'php.net,www.php.net'); +ini_set('session.trans_sid_hosts', 'php.net,www.php.net'); +ini_set('session.use_only_cookies', 1); +ini_set('session.use_cookies', 1); +ini_set('session.use_strict_mode', 0); +session_id('testid'); + +output_add_rewrite_var('<name>', '<value>'); +?> +Without session +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +ini_set('session.use_trans_sid', 0); +session_start(); +output_add_rewrite_var('<name>', '<value>'); +?> +Test use_trans_sid=0 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +session_commit(); +ini_set('session.use_trans_sid', 1); +output_reset_rewrite_vars(); +session_start(); +output_add_rewrite_var('<NAME>', '<VALUE>'); +?> +Test use_trans_sid=1 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +--EXPECT-- +Without session +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> + +Test use_trans_sid=0 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> + +Test use_trans_sid=1 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="http://php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="http://php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt new file mode 100644 index 0000000000..894fb1e838 --- /dev/null +++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic3.phpt @@ -0,0 +1,120 @@ +--TEST-- +Test output_add_rewrite_var() function basic feature +--SKIPIF-- +--INI-- +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" +--FILE-- +<?php + ob_start(); +// Common setting +ini_set('url_rewriter.hosts', 'example.com'); +ini_set('session.use_only_cookies', 0); +ini_set('session.use_cookies', 0); +ini_set('session.use_strict_mode', 0); +session_id('testid'); + +output_add_rewrite_var('<name>', '<value>'); +?> +Without session +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +ini_set('session.use_trans_sid', 0); +session_start(); +output_add_rewrite_var('<name>', '<value>'); +?> +Test use_trans_sid=0 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +session_commit(); +ini_set('session.use_trans_sid', 1); +output_reset_rewrite_vars(); +session_start(); +output_add_rewrite_var('<NAME>', '<VALUE>'); +?> +Test use_trans_sid=1 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +--EXPECT-- +Without session +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +Test use_trans_sid=0 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +Test use_trans_sid=1 +<a href="?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?PHPSESSID=testid&%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"><input type="hidden" name="PHPSESSID" value="testid" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /><input type="hidden" name="PHPSESSID" value="testid" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> diff --git a/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt b/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt new file mode 100644 index 0000000000..e9ffe7574d --- /dev/null +++ b/ext/standard/tests/general_functions/output_add_rewrite_var_basic4.phpt @@ -0,0 +1,120 @@ +--TEST-- +Test output_add_rewrite_var() function basic feature +--SKIPIF-- +--INI-- +session.trans_sid_tags="a=href,area=href,frame=src,form=" +url_rewriter.tags="a=href,area=href,frame=src,form=" +--FILE-- +<?php + ob_start(); +// Common setting +ini_set('url_rewriter.hosts', 'example.com'); +ini_set('session.use_only_cookies', 1); +ini_set('session.use_cookies', 1); +ini_set('session.use_strict_mode', 0); +session_id('testid'); + +output_add_rewrite_var('<name>', '<value>'); +?> +Without session +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +ini_set('session.use_trans_sid', 0); +session_start(); +output_add_rewrite_var('<name>', '<value>'); +?> +Test use_trans_sid=0 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +<?php +session_commit(); +ini_set('session.use_trans_sid', 1); +output_reset_rewrite_vars(); +session_start(); +output_add_rewrite_var('<NAME>', '<VALUE>'); +?> +Test use_trans_sid=1 +<a href=""> </a> +<a href="./foo.php"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +--EXPECT-- +Without session +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +Test use_trans_sid=0 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> + +Test use_trans_sid=1 +<a href="?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="./foo.php?%3CNAME%3E=%3CVALUE%3E"> </a> +<a href="//php.net/foo.php"> </a> +<a href="http://php.net/foo.php"> </a> +<a href="bad://php.net/foo.php"> </a> +<a href="//www.php.net/foo.php"> </a> + +<form method="get"> </form> +<form action="./foo.php" method="get"><input type="hidden" name="<NAME>" value="<VALUE>" /> </a> +<form action="//php.net/bar.php" method="get"> </a> +<form action="http://php.net/bar.php" method="get"> </a> +<form action="bad://php.net/bar.php" method="get"> </a> +<form action="//www.php.net/bar.php" method="get"> </a> diff --git a/ext/standard/url_scanner_ex.c b/ext/standard/url_scanner_ex.c index 07ebbe09a2..04a55e22cd 100644 --- a/ext/standard/url_scanner_ex.c +++ b/ext/standard/url_scanner_ex.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.14.3 */ #line 1 "ext/standard/url_scanner_ex.re" /* +----------------------------------------------------------------------+ @@ -15,6 +15,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Sascha Schumann <sascha@schumann.cx> | + | Yasuo Ohgaki <yohgaki@ohgaki.net> | +----------------------------------------------------------------------+ */ @@ -33,11 +34,14 @@ #include <stdlib.h> #include <string.h> +#include "SAPI.h" #include "php_ini.h" #include "php_globals.h" +#include "php_string.h" #define STATE_TAG SOME_OTHER_STATE_TAG #include "basic_functions.h" #include "url.h" +#include "html.h" #undef STATE_TAG #define url_scanner url_scanner_ex @@ -49,14 +53,18 @@ static void tag_dtor(zval *zv) free(Z_PTR_P(zv)); } -static PHP_INI_MH(OnUpdateTags) +static int php_ini_on_update_tags(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type) { url_adapt_state_ex_t *ctx; char *key; char *tmp; char *lasts = NULL; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); @@ -72,8 +80,8 @@ static PHP_INI_MH(OnUpdateTags) zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1); for (key = php_strtok_r(tmp, ",", &lasts); - key; - key = php_strtok_r(NULL, ",", &lasts)) { + key; + key = php_strtok_r(NULL, ",", &lasts)) { char *val; val = strchr(key, '='); @@ -82,11 +90,10 @@ static PHP_INI_MH(OnUpdateTags) size_t keylen; *val++ = '\0'; - for (q = key; *q; q++) + for (q = key; *q; q++) { *q = tolower(*q); + } keylen = q - key; - /* key is stored withOUT NUL - val is stored WITH NUL */ zend_hash_str_add_mem(ctx->tags, key, keylen, val, strlen(val)+1); } } @@ -96,11 +103,73 @@ static PHP_INI_MH(OnUpdateTags) return SUCCESS; } +static PHP_INI_MH(OnUpdateSessionTags) +{ + return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1); +} + +static PHP_INI_MH(OnUpdateOutputTags) +{ + return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0); +} + +static int php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type) +{ + HashTable *hosts; + char *key; + char *tmp; + char *lasts = NULL; + + if (type) { + hosts = &BG(url_adapt_session_hosts_ht); + } else { + hosts = &BG(url_adapt_output_hosts_ht); + } + zend_hash_clean(hosts); + + /* Use user supplied host whitelist */ + tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + for (key = php_strtok_r(tmp, ",", &lasts); + key; + key = php_strtok_r(NULL, ",", &lasts)) { + size_t keylen; + zend_string *tmp_key; + char *q; + + for (q = key; *q; q++) { + *q = tolower(*q); + } + keylen = q - key; + if (keylen > 0) { + tmp_key = zend_string_init(key, keylen, 0); + zend_hash_add_empty_element(hosts, tmp_key); + zend_string_release(tmp_key); + } + } + efree(tmp); + + return SUCCESS; +} + +static PHP_INI_MH(OnUpdateSessionHosts) +{ + return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1); +} + +static PHP_INI_MH(OnUpdateOutputHosts) +{ + return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0); +} + +/* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */ PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("url_rewriter.tags", "a=href,area=href,frame=src,form=,fieldset=", PHP_INI_ALL, OnUpdateTags, url_adapt_state_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("session.trans_sid_tags", "a=href,area=href,frame=src,form=", PHP_INI_ALL, OnUpdateSessionTags, url_adapt_session_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals) PHP_INI_END() -#line 107 "ext/standard/url_scanner_ex.re" +#line 176 "ext/standard/url_scanner_ex.re" #define YYFILL(n) goto done @@ -111,106 +180,101 @@ PHP_INI_END() static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator) { - register const char *p, *q; - const char *bash = NULL; - const char *sep = "?"; - - q = (p = ZSTR_VAL(url->s)) + ZSTR_LEN(url->s); - -scan: + php_url *url_parts; + char *tmp; + size_t tmp_len; -#line 123 "ext/standard/url_scanner_ex.c" -{ - YYCTYPE yych; - static const unsigned char yybm[] = { - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 128, 128, 128, 128, 0, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - }; + smart_str_0(url); /* FIXME: Bug #70480 php_url_parse_ex() crashes by processing chars exceed len */ + url_parts = php_url_parse_ex(ZSTR_VAL(url->s), ZSTR_LEN(url->s)); - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy8; - } - if (yych <= '9') goto yy6; - if (yych >= ';') goto yy4; - ++YYCURSOR; -#line 125 "ext/standard/url_scanner_ex.re" - { smart_str_append_smart_str(dest, url); return; } -#line 171 "ext/standard/url_scanner_ex.c" -yy4: - ++YYCURSOR; -#line 126 "ext/standard/url_scanner_ex.re" - { sep = separator; goto scan; } -#line 176 "ext/standard/url_scanner_ex.c" -yy6: - ++YYCURSOR; -#line 127 "ext/standard/url_scanner_ex.re" - { bash = p - 1; goto done; } -#line 181 "ext/standard/url_scanner_ex.c" -yy8: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yybm[0+yych] & 128) { - goto yy8; + /* Ignore malformed URLs */ + if (!url_parts) { + smart_str_append_smart_str(dest, url); + return; } -#line 128 "ext/standard/url_scanner_ex.re" - { goto scan; } -#line 191 "ext/standard/url_scanner_ex.c" -} -#line 129 "ext/standard/url_scanner_ex.re" -done: - - /* Don't modify URLs of the format "#mark" */ - if (bash && bash - ZSTR_VAL(url->s) == 0) { + /* Check protocol. Only http/https is allowed. */ + if (url_parts->scheme + && strcasecmp("http", url_parts->scheme) + && strcasecmp("https", url_parts->scheme)) { smart_str_append_smart_str(dest, url); + php_url_free(url_parts); return; } - if (bash) - smart_str_appendl(dest, ZSTR_VAL(url->s), bash - ZSTR_VAL(url->s)); - else + /* Check host whitelist. If it's not listed, do nothing. */ + if (url_parts->host + && (tmp_len = strlen(url_parts->host)) + && (tmp = php_strtolower(url_parts->host, tmp_len)) + && !zend_hash_str_find(&BG(url_adapt_session_hosts_ht), tmp, tmp_len)) { smart_str_append_smart_str(dest, url); + php_url_free(url_parts); + return; + } - smart_str_appends(dest, sep); - smart_str_append_smart_str(dest, url_app); + /* + * When URL does not have path and query string add "/?". + * i.e. If URL is only "?foo=bar", should not add "/?". + */ + if (!url_parts->path && !url_parts->query) { + /* URL is http://php.net or like */ + smart_str_append_smart_str(dest, url); + smart_str_appendc(dest, '/'); + smart_str_appendc(dest, '?'); + smart_str_append_smart_str(dest, url_app); + /* There should not be fragment. Just return */ + php_url_free(url_parts); + return; + } - if (bash) - smart_str_appendl(dest, bash, q - bash); + if (url_parts->scheme) { + smart_str_appends(dest, url_parts->scheme); + smart_str_appends(dest, "://"); + } else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') { + smart_str_appends(dest, "//"); + } + if (url_parts->user) { + smart_str_appends(dest, url_parts->user); + if (url_parts->pass) { + smart_str_appends(dest, url_parts->pass); + smart_str_appendc(dest, ':'); + } + smart_str_appendc(dest, '@'); + } + if (url_parts->host) { + smart_str_appends(dest, url_parts->host); + } + if (url_parts->port) { + smart_str_appendc(dest, ':'); + smart_str_append_unsigned(dest, (long)url_parts->port); + } + if (url_parts->path) { + smart_str_appends(dest, url_parts->path); + } + smart_str_appendc(dest, '?'); + if (url_parts->query) { + smart_str_appends(dest, url_parts->query); + smart_str_appends(dest, separator); + smart_str_append_smart_str(dest, url_app); + } else { + smart_str_append_smart_str(dest, url_app); + } + if (url_parts->fragment) { + smart_str_appendc(dest, '#'); + smart_str_appends(dest, url_parts->fragment); + } + php_url_free(url_parts); } +enum { + TAG_NORMAL = 0, + TAG_FORM +}; + +enum { + ATTR_NORMAL = 0, + ATTR_ACTION +}; #undef YYFILL #undef YYCTYPE @@ -222,18 +286,24 @@ static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type) { char f = 0; - if (strncasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data, ZSTR_LEN(ctx->arg.s)) == 0) + /* arg.s is string WITHOUT NUL. + To avoid partial match, NUL is added here */ + ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0'; + if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) { f = 1; + } - if (quotes) + if (quotes) { smart_str_appendc(&ctx->result, type); + } if (f) { append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output); } else { smart_str_append_smart_str(&ctx->result, &ctx->val); } - if (quotes) + if (quotes) { smart_str_appendc(&ctx->result, type); + } } enum { @@ -267,11 +337,79 @@ static inline void passthru(STD_PARA) smart_str_appendl(&ctx->result, start, YYCURSOR - start); } + +static int check_http_host(char *target) +{ + zval *host, *tmp; + zend_string *host_tmp; + char *colon; + + if ((tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))) && + (host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) && + Z_TYPE_P(host) == IS_STRING) { + host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0); + /* HTTP_HOST could be 'localhost:8888' etc. */ + colon = strchr(ZSTR_VAL(host_tmp), ':'); + if (colon) { + ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp); + ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0'; + } + if (!strcasecmp(ZSTR_VAL(host_tmp), target)) { + zend_string_release(host_tmp); + return SUCCESS; + } + zend_string_release(host_tmp); + } + return FAILURE; +} + +static int check_host_whitelist(url_adapt_state_ex_t *ctx) +{ + php_url *url_parts = NULL; + HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht); + + ZEND_ASSERT(ctx->tag_type == TAG_FORM); + + if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) { + url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s)); + } else { + return SUCCESS; /* empty URL is valid */ + } + + if (!url_parts) { + return FAILURE; + } + if (url_parts->scheme) { + /* Only http/https should be handled. + A bit hacky check this here, but saves a URL parse. */ + if (strcasecmp(url_parts->scheme, "http") && + strcasecmp(url_parts->scheme, "https")) { + php_url_free(url_parts); + return FAILURE; + } + } + if (!url_parts->host) { + php_url_free(url_parts); + return SUCCESS; + } + if (!zend_hash_num_elements(allowed_hosts) && + check_http_host(url_parts->host) == SUCCESS) { + php_url_free(url_parts); + return SUCCESS; + } + if (!zend_hash_str_find(allowed_hosts, + url_parts->host, + strlen(url_parts->host))) { + php_url_free(url_parts); + return FAILURE; + } + php_url_free(url_parts); + return SUCCESS; +} + /* - * This function appends a hidden input field after a <form> or - * <fieldset>. The latter is important for XHTML. + * This function appends a hidden input field after a <form>. */ - static void handle_form(STD_PARA) { int doit = 0; @@ -279,32 +417,16 @@ static void handle_form(STD_PARA) if (ZSTR_LEN(ctx->form_app.s) > 0) { switch (ZSTR_LEN(ctx->tag.s)) { case sizeof("form") - 1: - if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", sizeof("form") - 1)) { - doit = 1; - } - if (doit && ctx->val.s && ctx->lookup_data && *ctx->lookup_data) { - char *e, *p = (char *)zend_memnstr(ZSTR_VAL(ctx->val.s), "://", sizeof("://") - 1, ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)); - if (p) { - e = memchr(p, '/', (ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)) - p); - if (!e) { - e = ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s); - } - if ((e - p) && strncasecmp(p, ctx->lookup_data, (e - p))) { - doit = 0; - } - } - } - break; - - case sizeof("fieldset") - 1: - if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "fieldset", sizeof("fieldset") - 1)) { + if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s)) + && check_host_whitelist(ctx) == SUCCESS) { doit = 1; } break; } + } - if (doit) - smart_str_append_smart_str(&ctx->result, &ctx->form_app); + if (doit) { + smart_str_append_smart_str(&ctx->result, &ctx->form_app); } } @@ -327,8 +449,15 @@ static inline void handle_tag(STD_PARA) for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++) ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]); /* intentionally using str_find here, in case the hash value is set, but the string val is changed later */ - if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) + if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) { ok = 1; + if (ZSTR_LEN(ctx->tag.s) == sizeof("form")-1 + && !strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))) { + ctx->tag_type = TAG_FORM; + } else { + ctx->tag_type = TAG_NORMAL; + } + } STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN; } @@ -338,11 +467,20 @@ static inline void handle_arg(STD_PARA) ZSTR_LEN(ctx->arg.s) = 0; } smart_str_appendl(&ctx->arg, start, YYCURSOR - start); + if (ctx->tag_type == TAG_FORM && + strncasecmp(ZSTR_VAL(ctx->arg.s), "action", ZSTR_LEN(ctx->arg.s)) == 0) { + ctx->attr_type = ATTR_ACTION; + } else { + ctx->attr_type = ATTR_NORMAL; + } } static inline void handle_val(STD_PARA, char quotes, char type) { smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2); + if (ctx->tag_type == TAG_FORM && ctx->attr_type == ATTR_ACTION) { + smart_str_setl(&ctx->attr_val, start + quotes, YYCURSOR - start - quotes * 2); + } tag_arg(ctx, quotes, type); } @@ -374,7 +512,7 @@ state_plain_begin: state_plain: start = YYCURSOR; -#line 378 "ext/standard/url_scanner_ex.c" +#line 516 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -411,33 +549,34 @@ state_plain: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, }; + if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; if (yybm[0+yych] & 128) { - goto yy15; + goto yy4; } ++YYCURSOR; -#line 313 "ext/standard/url_scanner_ex.re" +#line 518 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; } -#line 423 "ext/standard/url_scanner_ex.c" -yy15: +#line 562 "ext/standard/url_scanner_ex.c" +yy4: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; if (yybm[0+yych] & 128) { - goto yy15; + goto yy4; } -#line 314 "ext/standard/url_scanner_ex.re" +#line 519 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); goto state_plain; } -#line 433 "ext/standard/url_scanner_ex.c" +#line 572 "ext/standard/url_scanner_ex.c" } -#line 315 "ext/standard/url_scanner_ex.re" +#line 520 "ext/standard/url_scanner_ex.re" state_tag: start = YYCURSOR; -#line 441 "ext/standard/url_scanner_ex.c" +#line 580 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -477,36 +616,36 @@ state_tag: if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; if (yych <= '@') { - if (yych != ':') goto yy22; + if (yych != ':') goto yy11; } else { - if (yych <= 'Z') goto yy20; - if (yych <= '`') goto yy22; - if (yych >= '{') goto yy22; + if (yych <= 'Z') goto yy9; + if (yych <= '`') goto yy11; + if (yych >= '{') goto yy11; } -yy20: +yy9: ++YYCURSOR; yych = *YYCURSOR; - goto yy25; -yy21: -#line 320 "ext/standard/url_scanner_ex.re" + goto yy14; +yy10: +#line 525 "ext/standard/url_scanner_ex.re" { handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; } -#line 494 "ext/standard/url_scanner_ex.c" -yy22: +#line 633 "ext/standard/url_scanner_ex.c" +yy11: ++YYCURSOR; -#line 321 "ext/standard/url_scanner_ex.re" +#line 526 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); goto state_plain_begin; } -#line 499 "ext/standard/url_scanner_ex.c" -yy24: +#line 638 "ext/standard/url_scanner_ex.c" +yy13: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy25: +yy14: if (yybm[0+yych] & 128) { - goto yy24; + goto yy13; } - goto yy21; + goto yy10; } -#line 322 "ext/standard/url_scanner_ex.re" +#line 527 "ext/standard/url_scanner_ex.re" state_next_arg_begin: @@ -515,7 +654,7 @@ state_next_arg_begin: state_next_arg: start = YYCURSOR; -#line 519 "ext/standard/url_scanner_ex.c" +#line 658 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -556,76 +695,76 @@ state_next_arg: yych = *YYCURSOR; if (yych <= '.') { if (yych <= '\f') { - if (yych <= 0x08) goto yy36; - if (yych <= '\v') goto yy32; - goto yy36; + if (yych <= 0x08) goto yy25; + if (yych <= '\v') goto yy21; + goto yy25; } else { - if (yych <= '\r') goto yy32; - if (yych == ' ') goto yy32; - goto yy36; + if (yych <= '\r') goto yy21; + if (yych == ' ') goto yy21; + goto yy25; } } else { if (yych <= '@') { - if (yych <= '/') goto yy28; - if (yych == '>') goto yy30; - goto yy36; + if (yych <= '/') goto yy17; + if (yych == '>') goto yy19; + goto yy25; } else { - if (yych <= 'Z') goto yy34; - if (yych <= '`') goto yy36; - if (yych <= 'z') goto yy34; - goto yy36; + if (yych <= 'Z') goto yy23; + if (yych <= '`') goto yy25; + if (yych <= 'z') goto yy23; + goto yy25; } } -yy28: +yy17: ++YYCURSOR; - if ((yych = *YYCURSOR) == '>') goto yy39; -yy29: -#line 333 "ext/standard/url_scanner_ex.re" + if ((yych = *YYCURSOR) == '>') goto yy28; +yy18: +#line 538 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); goto state_plain_begin; } -#line 586 "ext/standard/url_scanner_ex.c" -yy30: +#line 725 "ext/standard/url_scanner_ex.c" +yy19: ++YYCURSOR; -yy31: -#line 330 "ext/standard/url_scanner_ex.re" +yy20: +#line 535 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; } -#line 592 "ext/standard/url_scanner_ex.c" -yy32: +#line 731 "ext/standard/url_scanner_ex.c" +yy21: ++YYCURSOR; yych = *YYCURSOR; - goto yy38; -yy33: -#line 331 "ext/standard/url_scanner_ex.re" + goto yy27; +yy22: +#line 536 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); goto state_next_arg; } -#line 600 "ext/standard/url_scanner_ex.c" -yy34: +#line 739 "ext/standard/url_scanner_ex.c" +yy23: ++YYCURSOR; -#line 332 "ext/standard/url_scanner_ex.re" +#line 537 "ext/standard/url_scanner_ex.re" { --YYCURSOR; STATE = STATE_ARG; goto state_arg; } -#line 605 "ext/standard/url_scanner_ex.c" -yy36: +#line 744 "ext/standard/url_scanner_ex.c" +yy25: yych = *++YYCURSOR; - goto yy29; -yy37: + goto yy18; +yy26: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy38: +yy27: if (yybm[0+yych] & 128) { - goto yy37; + goto yy26; } - goto yy33; -yy39: + goto yy22; +yy28: ++YYCURSOR; yych = *YYCURSOR; - goto yy31; + goto yy20; } -#line 334 "ext/standard/url_scanner_ex.re" +#line 539 "ext/standard/url_scanner_ex.re" state_arg: start = YYCURSOR; -#line 629 "ext/standard/url_scanner_ex.c" +#line 768 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -664,40 +803,40 @@ state_arg: }; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; - if (yych <= '@') goto yy44; - if (yych <= 'Z') goto yy42; - if (yych <= '`') goto yy44; - if (yych >= '{') goto yy44; -yy42: + if (yych <= '@') goto yy33; + if (yych <= 'Z') goto yy31; + if (yych <= '`') goto yy33; + if (yych >= '{') goto yy33; +yy31: ++YYCURSOR; yych = *YYCURSOR; - goto yy47; -yy43: -#line 339 "ext/standard/url_scanner_ex.re" + goto yy36; +yy32: +#line 544 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; } -#line 679 "ext/standard/url_scanner_ex.c" -yy44: +#line 818 "ext/standard/url_scanner_ex.c" +yy33: ++YYCURSOR; -#line 340 "ext/standard/url_scanner_ex.re" +#line 545 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; } -#line 684 "ext/standard/url_scanner_ex.c" -yy46: +#line 823 "ext/standard/url_scanner_ex.c" +yy35: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy47: +yy36: if (yybm[0+yych] & 128) { - goto yy46; + goto yy35; } - goto yy43; + goto yy32; } -#line 341 "ext/standard/url_scanner_ex.re" +#line 546 "ext/standard/url_scanner_ex.re" state_before_val: start = YYCURSOR; -#line 701 "ext/standard/url_scanner_ex.c" +#line 840 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -736,54 +875,54 @@ state_before_val: }; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; - if (yych == ' ') goto yy50; - if (yych == '=') goto yy52; - goto yy54; -yy50: + if (yych == ' ') goto yy39; + if (yych == '=') goto yy41; + goto yy43; +yy39: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ' ') goto yy57; - if (yych == '=') goto yy55; -yy51: -#line 347 "ext/standard/url_scanner_ex.re" + if (yych == ' ') goto yy46; + if (yych == '=') goto yy44; +yy40: +#line 552 "ext/standard/url_scanner_ex.re" { --YYCURSOR; goto state_next_arg_begin; } -#line 750 "ext/standard/url_scanner_ex.c" -yy52: +#line 889 "ext/standard/url_scanner_ex.c" +yy41: ++YYCURSOR; yych = *YYCURSOR; - goto yy56; -yy53: -#line 346 "ext/standard/url_scanner_ex.re" + goto yy45; +yy42: +#line 551 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; } -#line 758 "ext/standard/url_scanner_ex.c" -yy54: +#line 897 "ext/standard/url_scanner_ex.c" +yy43: yych = *++YYCURSOR; - goto yy51; -yy55: + goto yy40; +yy44: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy56: +yy45: if (yybm[0+yych] & 128) { - goto yy55; + goto yy44; } - goto yy53; -yy57: + goto yy42; +yy46: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; - if (yych == ' ') goto yy57; - if (yych == '=') goto yy55; + if (yych == ' ') goto yy46; + if (yych == '=') goto yy44; YYCURSOR = YYMARKER; - goto yy51; + goto yy40; } -#line 348 "ext/standard/url_scanner_ex.re" +#line 553 "ext/standard/url_scanner_ex.re" state_val: start = YYCURSOR; -#line 787 "ext/standard/url_scanner_ex.c" +#line 926 "ext/standard/url_scanner_ex.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -824,85 +963,85 @@ state_val: yych = *YYCURSOR; if (yych <= ' ') { if (yych <= '\f') { - if (yych <= 0x08) goto yy65; - if (yych <= '\n') goto yy67; - goto yy65; + if (yych <= 0x08) goto yy54; + if (yych <= '\n') goto yy56; + goto yy54; } else { - if (yych <= '\r') goto yy67; - if (yych <= 0x1F) goto yy65; - goto yy67; + if (yych <= '\r') goto yy56; + if (yych <= 0x1F) goto yy54; + goto yy56; } } else { if (yych <= '&') { - if (yych != '"') goto yy65; + if (yych != '"') goto yy54; } else { - if (yych <= '\'') goto yy64; - if (yych == '>') goto yy67; - goto yy65; + if (yych <= '\'') goto yy53; + if (yych == '>') goto yy56; + goto yy54; } } yych = *(YYMARKER = ++YYCURSOR); - if (yych != '>') goto yy76; -yy63: -#line 357 "ext/standard/url_scanner_ex.re" + if (yych != '>') goto yy65; +yy52: +#line 562 "ext/standard/url_scanner_ex.re" { passthru(STD_ARGS); goto state_next_arg_begin; } -#line 850 "ext/standard/url_scanner_ex.c" -yy64: +#line 989 "ext/standard/url_scanner_ex.c" +yy53: yych = *(YYMARKER = ++YYCURSOR); - if (yych == '>') goto yy63; - goto yy71; -yy65: + if (yych == '>') goto yy52; + goto yy60; +yy54: ++YYCURSOR; yych = *YYCURSOR; - goto yy69; -yy66: -#line 356 "ext/standard/url_scanner_ex.re" + goto yy58; +yy55: +#line 561 "ext/standard/url_scanner_ex.re" { handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; } -#line 862 "ext/standard/url_scanner_ex.c" -yy67: +#line 1001 "ext/standard/url_scanner_ex.c" +yy56: yych = *++YYCURSOR; - goto yy63; -yy68: + goto yy52; +yy57: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy69: +yy58: if (yybm[0+yych] & 32) { - goto yy68; + goto yy57; } - goto yy66; -yy70: + goto yy55; +yy59: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy71: +yy60: if (yybm[0+yych] & 64) { - goto yy70; + goto yy59; } - if (yych <= '=') goto yy73; -yy72: + if (yych <= '\'') goto yy62; +yy61: YYCURSOR = YYMARKER; - goto yy63; -yy73: + goto yy52; +yy62: ++YYCURSOR; -#line 355 "ext/standard/url_scanner_ex.re" +#line 560 "ext/standard/url_scanner_ex.re" { handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; } -#line 891 "ext/standard/url_scanner_ex.c" -yy75: +#line 1030 "ext/standard/url_scanner_ex.c" +yy64: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy76: +yy65: if (yybm[0+yych] & 128) { - goto yy75; + goto yy64; } - if (yych >= '>') goto yy72; + if (yych >= '#') goto yy61; ++YYCURSOR; -#line 354 "ext/standard/url_scanner_ex.re" +#line 559 "ext/standard/url_scanner_ex.re" { handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; } -#line 904 "ext/standard/url_scanner_ex.c" +#line 1043 "ext/standard/url_scanner_ex.c" } -#line 358 "ext/standard/url_scanner_ex.re" +#line 563 "ext/standard/url_scanner_ex.re" stop: @@ -919,7 +1058,7 @@ stop: } -PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode) +PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode) { char *result; smart_str surl = {0}; @@ -929,7 +1068,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co smart_str_appendl(&surl, url, urllen); - if (urlencode) { + if (encode) { encoded = php_raw_url_encode(name, strlen(name)); smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); @@ -937,7 +1076,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co smart_str_appends(&url_app, name); } smart_str_appendc(&url_app, '='); - if (urlencode) { + if (encode) { encoded = php_raw_url_encode(value, strlen(value)); smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); @@ -958,13 +1097,10 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co } -static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush) +static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush, url_adapt_state_ex_t *ctx) { - url_adapt_state_ex_t *ctx; char *retval; - ctx = &BG(url_adapt_state_ex); - xx_mainloop(ctx, src, srclen); if (!ctx->result.s) { @@ -979,50 +1115,67 @@ static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_ *newlen += ZSTR_LEN(ctx->buf.s); smart_str_free(&ctx->buf); smart_str_free(&ctx->val); + smart_str_free(&ctx->attr_val); } retval = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s)); smart_str_free(&ctx->result); return retval; } -static int php_url_scanner_ex_activate(void) +static int php_url_scanner_ex_activate(int type) { url_adapt_state_ex_t *ctx; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } memset(ctx, 0, ((size_t) &((url_adapt_state_ex_t *)0)->tags)); return SUCCESS; } -static int php_url_scanner_ex_deactivate(void) +static int php_url_scanner_ex_deactivate(int type) { url_adapt_state_ex_t *ctx; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } smart_str_free(&ctx->result); smart_str_free(&ctx->buf); smart_str_free(&ctx->tag); smart_str_free(&ctx->arg); + smart_str_free(&ctx->attr_val); return SUCCESS; } -static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +static inline void php_url_scanner_session_handler_impl(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode, int type) { size_t len; + url_adapt_state_ex_t *url_state; - if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) { - *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0)); + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); + } + + if (ZSTR_LEN(url_state->url_app.s) != 0) { + *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0), url_state); if (sizeof(uint) < sizeof(size_t)) { if (len > UINT_MAX) len = UINT_MAX; } *handled_output_len = len; - } else if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) == 0) { - url_adapt_state_ex_t *ctx = &BG(url_adapt_state_ex); + } else if (ZSTR_LEN(url_state->url_app.s) == 0) { + url_adapt_state_ex_t *ctx = url_state; if (ctx->buf.s && ZSTR_LEN(ctx->buf.s)) { smart_str_append(&ctx->result, ctx->buf.s); smart_str_appendl(&ctx->result, output, output_len); @@ -1040,67 +1193,267 @@ static void php_url_scanner_output_handler(char *output, size_t output_len, char } } -PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode) +static void php_url_scanner_session_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +{ + php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 1); +} + +static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +{ + php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 0); +} + +static inline int php_url_scanner_add_var_impl(char *name, size_t name_len, char *value, size_t value_len, int encode, int type) { smart_str sname = {0}; smart_str svalue = {0}; + smart_str hname = {0}; + smart_str hvalue = {0}; zend_string *encoded; + url_adapt_state_ex_t *url_state; + php_output_handler_func_t handler; + + if (type) { + url_state = &BG(url_adapt_session_ex); + handler = php_url_scanner_session_handler; + } else { + url_state = &BG(url_adapt_output_ex); + handler = php_url_scanner_output_handler; + } - if (!BG(url_adapt_state_ex).active) { - php_url_scanner_ex_activate(); - php_output_start_internal(ZEND_STRL("URL-Rewriter"), php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS); - BG(url_adapt_state_ex).active = 1; + if (!url_state->active) { + php_url_scanner_ex_activate(type); + php_output_start_internal(ZEND_STRL("URL-Rewriter"), handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS); + url_state->active = 1; } - if (BG(url_adapt_state_ex).url_app.s && ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) { - smart_str_appends(&BG(url_adapt_state_ex).url_app, PG(arg_separator).output); + if (url_state->url_app.s && ZSTR_LEN(url_state->url_app.s) != 0) { + smart_str_appends(&url_state->url_app, PG(arg_separator).output); } - if (urlencode) { + if (encode) { encoded = php_raw_url_encode(name, name_len); - smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); - zend_string_free(encoded); + smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); encoded = php_raw_url_encode(value, value_len); - smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); - zend_string_free(encoded); + smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char*)name, name_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char*)value, value_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hvalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); } else { smart_str_appendl(&sname, name, name_len); smart_str_appendl(&svalue, value, value_len); + smart_str_appendl(&hname, name, name_len); + smart_str_appendl(&hvalue, value, value_len); } - smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &sname); - smart_str_appendc(&BG(url_adapt_state_ex).url_app, '='); - smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &svalue); + smart_str_append_smart_str(&url_state->url_app, &sname); + smart_str_appendc(&url_state->url_app, '='); + smart_str_append_smart_str(&url_state->url_app, &svalue); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "<input type=\"hidden\" name=\""); - smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &sname); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" value=\""); - smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &svalue); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" />"); + smart_str_appends(&url_state->form_app, "<input type=\"hidden\" name=\""); + smart_str_append_smart_str(&url_state->form_app, &hname); + smart_str_appends(&url_state->form_app, "\" value=\""); + smart_str_append_smart_str(&url_state->form_app, &hvalue); + smart_str_appends(&url_state->form_app, "\" />"); smart_str_free(&sname); smart_str_free(&svalue); + smart_str_free(&hname); + smart_str_free(&hvalue); return SUCCESS; } -PHPAPI int php_url_scanner_reset_vars(void) + +PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode) +{ + return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 1); +} + + +PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode) { - if (BG(url_adapt_state_ex).form_app.s) { - ZSTR_LEN(BG(url_adapt_state_ex).form_app.s) = 0; + return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 0); +} + + +static inline void php_url_scanner_reset_vars_impl(int type) { + url_adapt_state_ex_t *url_state; + + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); } - if (BG(url_adapt_state_ex).url_app.s) { - ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) = 0; + + if (url_state->form_app.s) { + ZSTR_LEN(url_state->form_app.s) = 0; + } + if (url_state->url_app.s) { + ZSTR_LEN(url_state->url_app.s) = 0; } +} + +PHPAPI int php_url_scanner_reset_session_vars(void) +{ + php_url_scanner_reset_vars_impl(1); return SUCCESS; } + +PHPAPI int php_url_scanner_reset_vars(void) +{ + php_url_scanner_reset_vars_impl(0); + return SUCCESS; +} + + +static inline int php_url_scanner_reset_var_impl(zend_string *name, int encode, int type) +{ + char *start, *end, *limit; + size_t separator_len; + smart_str sname = {0}; + smart_str hname = {0}; + smart_str url_app = {0}; + smart_str form_app = {0}; + zend_string *encoded; + int ret = SUCCESS; + zend_bool sep_removed = 0; + url_adapt_state_ex_t *url_state; + + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); + } + + /* Short circuit check. Only check url_app. */ + if (!url_state->url_app.s || !ZSTR_LEN(url_state->url_app.s)) { + return SUCCESS; + } + + if (encode) { + encoded = php_raw_url_encode(ZSTR_VAL(name), ZSTR_LEN(name)); + smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); + zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char *)ZSTR_VAL(name), ZSTR_LEN(name), 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); + zend_string_free(encoded); + } else { + smart_str_appendl(&sname, ZSTR_VAL(name), ZSTR_LEN(name)); + smart_str_appendl(&hname, ZSTR_VAL(name), ZSTR_LEN(name)); + } + smart_str_0(&sname); + smart_str_0(&hname); + + smart_str_append_smart_str(&url_app, &sname); + smart_str_appendc(&url_app, '='); + smart_str_0(&url_app); + + smart_str_appends(&form_app, "<input type=\"hidden\" name=\""); + smart_str_append_smart_str(&form_app, &hname); + smart_str_appends(&form_app, "\" value=\""); + smart_str_0(&form_app); + + /* Short circuit check. Only check url_app. */ + start = (char *) php_memnstr(ZSTR_VAL(url_state->url_app.s), + ZSTR_VAL(url_app.s), ZSTR_LEN(url_app.s), + ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s)); + if (!start) { + ret = FAILURE; + goto finish; + } + + /* Get end of url var */ + limit = ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s); + end = start + ZSTR_LEN(url_app.s); + separator_len = strlen(PG(arg_separator).output); + while (end < limit) { + if (!memcmp(end, PG(arg_separator).output, separator_len)) { + end += separator_len; + sep_removed = 1; + break; + } + end++; + } + /* Remove all when this is the only rewrite var */ + if (ZSTR_LEN(url_state->url_app.s) == end - start) { + php_url_scanner_reset_vars_impl(type); + goto finish; + } + /* Check preceeding separator */ + if (!sep_removed + && start - PG(arg_separator).output >= separator_len + && !memcmp(start - separator_len, PG(arg_separator).output, separator_len)) { + start -= separator_len; + } + /* Remove partially */ + memmove(start, end, + ZSTR_LEN(url_state->url_app.s) - (end - ZSTR_VAL(url_state->url_app.s))); + ZSTR_LEN(url_state->url_app.s) -= end - start; + ZSTR_VAL(url_state->url_app.s)[ZSTR_LEN(url_state->url_app.s)] = '\0'; + + /* Remove form var */ + start = (char *) php_memnstr(ZSTR_VAL(url_state->form_app.s), + ZSTR_VAL(form_app.s), ZSTR_LEN(form_app.s), + ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s)); + if (!start) { + /* Should not happen */ + ret = FAILURE; + php_url_scanner_reset_vars_impl(type); + goto finish; + } + /* Get end of form var */ + limit = ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s); + end = start + ZSTR_LEN(form_app.s); + while (end < limit) { + if (*end == '>') { + end += 1; + break; + } + end++; + } + /* Remove partially */ + memmove(start, end, + ZSTR_LEN(url_state->form_app.s) - (end - ZSTR_VAL(url_state->form_app.s))); + ZSTR_LEN(url_state->form_app.s) -= end - start; + ZSTR_VAL(url_state->form_app.s)[ZSTR_LEN(url_state->form_app.s)] = '\0'; + +finish: + smart_str_free(&url_app); + smart_str_free(&form_app); + smart_str_free(&sname); + smart_str_free(&hname); + return ret; +} + + +PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode) +{ + return php_url_scanner_reset_var_impl(name, encode, 1); +} + + +PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode) +{ + return php_url_scanner_reset_var_impl(name, encode, 0); +} + + PHP_MINIT_FUNCTION(url_scanner) { - BG(url_adapt_state_ex).tags = NULL; + BG(url_adapt_session_ex).tags = NULL; + BG(url_adapt_session_ex).form_app.s = BG(url_adapt_session_ex).url_app.s = NULL; + zend_hash_init(&BG(url_adapt_session_hosts_ht), 0, NULL, NULL, 1); - BG(url_adapt_state_ex).form_app.s = BG(url_adapt_state_ex).url_app.s = NULL; + BG(url_adapt_output_ex).tags = NULL; + BG(url_adapt_output_ex).form_app.s = BG(url_adapt_output_ex).url_app.s = NULL; + zend_hash_init(&BG(url_adapt_output_hosts_ht), 0, NULL, NULL, 1); + + BG(url_adapt_session_ex).type = 1; + BG(url_adapt_output_ex).type = 0; REGISTER_INI_ENTRIES(); return SUCCESS; @@ -1115,20 +1468,34 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner) PHP_RINIT_FUNCTION(url_scanner) { - BG(url_adapt_state_ex).active = 0; - + BG(url_adapt_session_ex).active = 0; + BG(url_adapt_session_ex).tag_type = 0; + BG(url_adapt_session_ex).attr_type = 0; + BG(url_adapt_output_ex).active = 0; + BG(url_adapt_output_ex).tag_type = 0; + BG(url_adapt_output_ex).attr_type = 0; return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(url_scanner) { - if (BG(url_adapt_state_ex).active) { - php_url_scanner_ex_deactivate(); - BG(url_adapt_state_ex).active = 0; + if (BG(url_adapt_session_ex).active) { + php_url_scanner_ex_deactivate(1); + BG(url_adapt_session_ex).active = 0; + BG(url_adapt_session_ex).tag_type = 0; + BG(url_adapt_session_ex).attr_type = 0; } - - smart_str_free(&BG(url_adapt_state_ex).form_app); - smart_str_free(&BG(url_adapt_state_ex).url_app); + smart_str_free(&BG(url_adapt_session_ex).form_app); + smart_str_free(&BG(url_adapt_session_ex).url_app); + + if (BG(url_adapt_output_ex).active) { + php_url_scanner_ex_deactivate(0); + BG(url_adapt_output_ex).active = 0; + BG(url_adapt_output_ex).tag_type = 0; + BG(url_adapt_output_ex).attr_type = 0; + } + smart_str_free(&BG(url_adapt_output_ex).form_app); + smart_str_free(&BG(url_adapt_output_ex).url_app); return SUCCESS; } diff --git a/ext/standard/url_scanner_ex.h b/ext/standard/url_scanner_ex.h index 900f2bb44d..55fe2bfc9c 100644 --- a/ext/standard/url_scanner_ex.h +++ b/ext/standard/url_scanner_ex.h @@ -27,8 +27,12 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner_ex); PHP_RINIT_FUNCTION(url_scanner_ex); PHP_RSHUTDOWN_FUNCTION(url_scanner_ex); -PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode); -PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode); +PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode); +PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode); +PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode); +PHPAPI int php_url_scanner_reset_session_vars(void); +PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode); +PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode); PHPAPI int php_url_scanner_reset_vars(void); #include "zend_smart_str_public.h" @@ -51,6 +55,11 @@ typedef struct { char *lookup_data; int state; + int type; + smart_str attr_val; + int tag_type; + int attr_type; + /* Everything above is zeroed in RINIT */ HashTable *tags; } url_adapt_state_ex_t; diff --git a/ext/standard/url_scanner_ex.re b/ext/standard/url_scanner_ex.re index 9eb0952750..e0e74d03fa 100644 --- a/ext/standard/url_scanner_ex.re +++ b/ext/standard/url_scanner_ex.re @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Sascha Schumann <sascha@schumann.cx> | + | Yasuo Ohgaki <yohgaki@ohgaki.net> | +----------------------------------------------------------------------+ */ @@ -31,11 +32,14 @@ #include <stdlib.h> #include <string.h> +#include "SAPI.h" #include "php_ini.h" #include "php_globals.h" +#include "php_string.h" #define STATE_TAG SOME_OTHER_STATE_TAG #include "basic_functions.h" #include "url.h" +#include "html.h" #undef STATE_TAG #define url_scanner url_scanner_ex @@ -47,14 +51,18 @@ static void tag_dtor(zval *zv) free(Z_PTR_P(zv)); } -static PHP_INI_MH(OnUpdateTags) +static int php_ini_on_update_tags(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type) { url_adapt_state_ex_t *ctx; char *key; char *tmp; char *lasts = NULL; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); @@ -70,8 +78,8 @@ static PHP_INI_MH(OnUpdateTags) zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1); for (key = php_strtok_r(tmp, ",", &lasts); - key; - key = php_strtok_r(NULL, ",", &lasts)) { + key; + key = php_strtok_r(NULL, ",", &lasts)) { char *val; val = strchr(key, '='); @@ -80,11 +88,10 @@ static PHP_INI_MH(OnUpdateTags) size_t keylen; *val++ = '\0'; - for (q = key; *q; q++) + for (q = key; *q; q++) { *q = tolower(*q); + } keylen = q - key; - /* key is stored withOUT NUL - val is stored WITH NUL */ zend_hash_str_add_mem(ctx->tags, key, keylen, val, strlen(val)+1); } } @@ -94,8 +101,70 @@ static PHP_INI_MH(OnUpdateTags) return SUCCESS; } +static PHP_INI_MH(OnUpdateSessionTags) +{ + return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1); +} + +static PHP_INI_MH(OnUpdateOutputTags) +{ + return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0); +} + +static int php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type) +{ + HashTable *hosts; + char *key; + char *tmp; + char *lasts = NULL; + + if (type) { + hosts = &BG(url_adapt_session_hosts_ht); + } else { + hosts = &BG(url_adapt_output_hosts_ht); + } + zend_hash_clean(hosts); + + /* Use user supplied host whitelist */ + tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + for (key = php_strtok_r(tmp, ",", &lasts); + key; + key = php_strtok_r(NULL, ",", &lasts)) { + size_t keylen; + zend_string *tmp_key; + char *q; + + for (q = key; *q; q++) { + *q = tolower(*q); + } + keylen = q - key; + if (keylen > 0) { + tmp_key = zend_string_init(key, keylen, 0); + zend_hash_add_empty_element(hosts, tmp_key); + zend_string_release(tmp_key); + } + } + efree(tmp); + + return SUCCESS; +} + +static PHP_INI_MH(OnUpdateSessionHosts) +{ + return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1); +} + +static PHP_INI_MH(OnUpdateOutputHosts) +{ + return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0); +} + +/* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */ PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("url_rewriter.tags", "a=href,area=href,frame=src,form=,fieldset=", PHP_INI_ALL, OnUpdateTags, url_adapt_state_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("session.trans_sid_tags", "a=href,area=href,frame=src,form=", PHP_INI_ALL, OnUpdateSessionTags, url_adapt_session_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals) + STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals) PHP_INI_END() /*!re2c @@ -114,39 +183,101 @@ alphadash = ([a-zA-Z] | "-"); static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator) { - register const char *p, *q; - const char *bash = NULL; - const char *sep = "?"; + php_url *url_parts; + char *tmp; + size_t tmp_len; - q = (p = ZSTR_VAL(url->s)) + ZSTR_LEN(url->s); + smart_str_0(url); /* FIXME: Bug #70480 php_url_parse_ex() crashes by processing chars exceed len */ + url_parts = php_url_parse_ex(ZSTR_VAL(url->s), ZSTR_LEN(url->s)); -scan: -/*!re2c - ":" { smart_str_append_smart_str(dest, url); return; } - "?" { sep = separator; goto scan; } - "#" { bash = p - 1; goto done; } - (any\[:?#])+ { goto scan; } -*/ -done: + /* Ignore malformed URLs */ + if (!url_parts) { + smart_str_append_smart_str(dest, url); + return; + } - /* Don't modify URLs of the format "#mark" */ - if (bash && bash - ZSTR_VAL(url->s) == 0) { + /* Check protocol. Only http/https is allowed. */ + if (url_parts->scheme + && strcasecmp("http", url_parts->scheme) + && strcasecmp("https", url_parts->scheme)) { smart_str_append_smart_str(dest, url); + php_url_free(url_parts); return; } - if (bash) - smart_str_appendl(dest, ZSTR_VAL(url->s), bash - ZSTR_VAL(url->s)); - else + /* Check host whitelist. If it's not listed, do nothing. */ + if (url_parts->host + && (tmp_len = strlen(url_parts->host)) + && (tmp = php_strtolower(url_parts->host, tmp_len)) + && !zend_hash_str_find(&BG(url_adapt_session_hosts_ht), tmp, tmp_len)) { smart_str_append_smart_str(dest, url); + php_url_free(url_parts); + return; + } - smart_str_appends(dest, sep); - smart_str_append_smart_str(dest, url_app); + /* + * When URL does not have path and query string add "/?". + * i.e. If URL is only "?foo=bar", should not add "/?". + */ + if (!url_parts->path && !url_parts->query) { + /* URL is http://php.net or like */ + smart_str_append_smart_str(dest, url); + smart_str_appendc(dest, '/'); + smart_str_appendc(dest, '?'); + smart_str_append_smart_str(dest, url_app); + /* There should not be fragment. Just return */ + php_url_free(url_parts); + return; + } - if (bash) - smart_str_appendl(dest, bash, q - bash); + if (url_parts->scheme) { + smart_str_appends(dest, url_parts->scheme); + smart_str_appends(dest, "://"); + } else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') { + smart_str_appends(dest, "//"); + } + if (url_parts->user) { + smart_str_appends(dest, url_parts->user); + if (url_parts->pass) { + smart_str_appends(dest, url_parts->pass); + smart_str_appendc(dest, ':'); + } + smart_str_appendc(dest, '@'); + } + if (url_parts->host) { + smart_str_appends(dest, url_parts->host); + } + if (url_parts->port) { + smart_str_appendc(dest, ':'); + smart_str_append_unsigned(dest, (long)url_parts->port); + } + if (url_parts->path) { + smart_str_appends(dest, url_parts->path); + } + smart_str_appendc(dest, '?'); + if (url_parts->query) { + smart_str_appends(dest, url_parts->query); + smart_str_appends(dest, separator); + smart_str_append_smart_str(dest, url_app); + } else { + smart_str_append_smart_str(dest, url_app); + } + if (url_parts->fragment) { + smart_str_appendc(dest, '#'); + smart_str_appends(dest, url_parts->fragment); + } + php_url_free(url_parts); } +enum { + TAG_NORMAL = 0, + TAG_FORM +}; + +enum { + ATTR_NORMAL = 0, + ATTR_ACTION +}; #undef YYFILL #undef YYCTYPE @@ -158,18 +289,24 @@ static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type) { char f = 0; - if (strncasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data, ZSTR_LEN(ctx->arg.s)) == 0) + /* arg.s is string WITHOUT NUL. + To avoid partial match, NUL is added here */ + ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0'; + if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) { f = 1; + } - if (quotes) + if (quotes) { smart_str_appendc(&ctx->result, type); + } if (f) { append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output); } else { smart_str_append_smart_str(&ctx->result, &ctx->val); } - if (quotes) + if (quotes) { smart_str_appendc(&ctx->result, type); + } } enum { @@ -203,11 +340,79 @@ static inline void passthru(STD_PARA) smart_str_appendl(&ctx->result, start, YYCURSOR - start); } + +static int check_http_host(char *target) +{ + zval *host, *tmp; + zend_string *host_tmp; + char *colon; + + if ((tmp = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))) && + (host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) && + Z_TYPE_P(host) == IS_STRING) { + host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0); + /* HTTP_HOST could be 'localhost:8888' etc. */ + colon = strchr(ZSTR_VAL(host_tmp), ':'); + if (colon) { + ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp); + ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0'; + } + if (!strcasecmp(ZSTR_VAL(host_tmp), target)) { + zend_string_release(host_tmp); + return SUCCESS; + } + zend_string_release(host_tmp); + } + return FAILURE; +} + +static int check_host_whitelist(url_adapt_state_ex_t *ctx) +{ + php_url *url_parts = NULL; + HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht); + + ZEND_ASSERT(ctx->tag_type == TAG_FORM); + + if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) { + url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s)); + } else { + return SUCCESS; /* empty URL is valid */ + } + + if (!url_parts) { + return FAILURE; + } + if (url_parts->scheme) { + /* Only http/https should be handled. + A bit hacky check this here, but saves a URL parse. */ + if (strcasecmp(url_parts->scheme, "http") && + strcasecmp(url_parts->scheme, "https")) { + php_url_free(url_parts); + return FAILURE; + } + } + if (!url_parts->host) { + php_url_free(url_parts); + return SUCCESS; + } + if (!zend_hash_num_elements(allowed_hosts) && + check_http_host(url_parts->host) == SUCCESS) { + php_url_free(url_parts); + return SUCCESS; + } + if (!zend_hash_str_find(allowed_hosts, + url_parts->host, + strlen(url_parts->host))) { + php_url_free(url_parts); + return FAILURE; + } + php_url_free(url_parts); + return SUCCESS; +} + /* - * This function appends a hidden input field after a <form> or - * <fieldset>. The latter is important for XHTML. + * This function appends a hidden input field after a <form>. */ - static void handle_form(STD_PARA) { int doit = 0; @@ -215,32 +420,16 @@ static void handle_form(STD_PARA) if (ZSTR_LEN(ctx->form_app.s) > 0) { switch (ZSTR_LEN(ctx->tag.s)) { case sizeof("form") - 1: - if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", sizeof("form") - 1)) { - doit = 1; - } - if (doit && ctx->val.s && ctx->lookup_data && *ctx->lookup_data) { - char *e, *p = (char *)zend_memnstr(ZSTR_VAL(ctx->val.s), "://", sizeof("://") - 1, ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)); - if (p) { - e = memchr(p, '/', (ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s)) - p); - if (!e) { - e = ZSTR_VAL(ctx->val.s) + ZSTR_LEN(ctx->val.s); - } - if ((e - p) && strncasecmp(p, ctx->lookup_data, (e - p))) { - doit = 0; - } - } - } - break; - - case sizeof("fieldset") - 1: - if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "fieldset", sizeof("fieldset") - 1)) { + if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s)) + && check_host_whitelist(ctx) == SUCCESS) { doit = 1; } break; } + } - if (doit) - smart_str_append_smart_str(&ctx->result, &ctx->form_app); + if (doit) { + smart_str_append_smart_str(&ctx->result, &ctx->form_app); } } @@ -263,8 +452,15 @@ static inline void handle_tag(STD_PARA) for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++) ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]); /* intentionally using str_find here, in case the hash value is set, but the string val is changed later */ - if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) + if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) { ok = 1; + if (ZSTR_LEN(ctx->tag.s) == sizeof("form")-1 + && !strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))) { + ctx->tag_type = TAG_FORM; + } else { + ctx->tag_type = TAG_NORMAL; + } + } STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN; } @@ -274,11 +470,20 @@ static inline void handle_arg(STD_PARA) ZSTR_LEN(ctx->arg.s) = 0; } smart_str_appendl(&ctx->arg, start, YYCURSOR - start); + if (ctx->tag_type == TAG_FORM && + strncasecmp(ZSTR_VAL(ctx->arg.s), "action", ZSTR_LEN(ctx->arg.s)) == 0) { + ctx->attr_type = ATTR_ACTION; + } else { + ctx->attr_type = ATTR_NORMAL; + } } static inline void handle_val(STD_PARA, char quotes, char type) { smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2); + if (ctx->tag_type == TAG_FORM && ctx->attr_type == ATTR_ACTION) { + smart_str_setl(&ctx->attr_val, start + quotes, YYCURSOR - start - quotes * 2); + } tag_arg(ctx, quotes, type); } @@ -371,7 +576,7 @@ stop: } -PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int urlencode) +PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode) { char *result; smart_str surl = {0}; @@ -381,7 +586,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co smart_str_appendl(&surl, url, urllen); - if (urlencode) { + if (encode) { encoded = php_raw_url_encode(name, strlen(name)); smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); @@ -389,7 +594,7 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co smart_str_appends(&url_app, name); } smart_str_appendc(&url_app, '='); - if (urlencode) { + if (encode) { encoded = php_raw_url_encode(value, strlen(value)); smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); @@ -410,13 +615,10 @@ PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, co } -static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush) +static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush, url_adapt_state_ex_t *ctx) { - url_adapt_state_ex_t *ctx; char *retval; - ctx = &BG(url_adapt_state_ex); - xx_mainloop(ctx, src, srclen); if (!ctx->result.s) { @@ -431,50 +633,67 @@ static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_ *newlen += ZSTR_LEN(ctx->buf.s); smart_str_free(&ctx->buf); smart_str_free(&ctx->val); + smart_str_free(&ctx->attr_val); } retval = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s)); smart_str_free(&ctx->result); return retval; } -static int php_url_scanner_ex_activate(void) +static int php_url_scanner_ex_activate(int type) { url_adapt_state_ex_t *ctx; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } memset(ctx, 0, ((size_t) &((url_adapt_state_ex_t *)0)->tags)); return SUCCESS; } -static int php_url_scanner_ex_deactivate(void) +static int php_url_scanner_ex_deactivate(int type) { url_adapt_state_ex_t *ctx; - ctx = &BG(url_adapt_state_ex); + if (type) { + ctx = &BG(url_adapt_session_ex); + } else { + ctx = &BG(url_adapt_output_ex); + } smart_str_free(&ctx->result); smart_str_free(&ctx->buf); smart_str_free(&ctx->tag); smart_str_free(&ctx->arg); + smart_str_free(&ctx->attr_val); return SUCCESS; } -static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +static inline void php_url_scanner_session_handler_impl(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode, int type) { size_t len; + url_adapt_state_ex_t *url_state; + + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); + } - if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) { - *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0)); + if (ZSTR_LEN(url_state->url_app.s) != 0) { + *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0), url_state); if (sizeof(uint) < sizeof(size_t)) { if (len > UINT_MAX) len = UINT_MAX; } *handled_output_len = len; - } else if (ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) == 0) { - url_adapt_state_ex_t *ctx = &BG(url_adapt_state_ex); + } else if (ZSTR_LEN(url_state->url_app.s) == 0) { + url_adapt_state_ex_t *ctx = url_state; if (ctx->buf.s && ZSTR_LEN(ctx->buf.s)) { smart_str_append(&ctx->result, ctx->buf.s); smart_str_appendl(&ctx->result, output, output_len); @@ -492,67 +711,267 @@ static void php_url_scanner_output_handler(char *output, size_t output_len, char } } -PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int urlencode) +static void php_url_scanner_session_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +{ + php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 1); +} + +static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode) +{ + php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 0); +} + +static inline int php_url_scanner_add_var_impl(char *name, size_t name_len, char *value, size_t value_len, int encode, int type) { smart_str sname = {0}; smart_str svalue = {0}; + smart_str hname = {0}; + smart_str hvalue = {0}; zend_string *encoded; + url_adapt_state_ex_t *url_state; + php_output_handler_func_t handler; - if (!BG(url_adapt_state_ex).active) { - php_url_scanner_ex_activate(); - php_output_start_internal(ZEND_STRL("URL-Rewriter"), php_url_scanner_output_handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS); - BG(url_adapt_state_ex).active = 1; + if (type) { + url_state = &BG(url_adapt_session_ex); + handler = php_url_scanner_session_handler; + } else { + url_state = &BG(url_adapt_output_ex); + handler = php_url_scanner_output_handler; } - if (BG(url_adapt_state_ex).url_app.s && ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) != 0) { - smart_str_appends(&BG(url_adapt_state_ex).url_app, PG(arg_separator).output); + if (!url_state->active) { + php_url_scanner_ex_activate(type); + php_output_start_internal(ZEND_STRL("URL-Rewriter"), handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS); + url_state->active = 1; } - if (urlencode) { + if (url_state->url_app.s && ZSTR_LEN(url_state->url_app.s) != 0) { + smart_str_appends(&url_state->url_app, PG(arg_separator).output); + } + + if (encode) { encoded = php_raw_url_encode(name, name_len); - smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); - zend_string_free(encoded); + smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); encoded = php_raw_url_encode(value, value_len); - smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); - zend_string_free(encoded); + smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char*)name, name_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char*)value, value_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hvalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded); } else { smart_str_appendl(&sname, name, name_len); smart_str_appendl(&svalue, value, value_len); + smart_str_appendl(&hname, name, name_len); + smart_str_appendl(&hvalue, value, value_len); } - smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &sname); - smart_str_appendc(&BG(url_adapt_state_ex).url_app, '='); - smart_str_append_smart_str(&BG(url_adapt_state_ex).url_app, &svalue); + smart_str_append_smart_str(&url_state->url_app, &sname); + smart_str_appendc(&url_state->url_app, '='); + smart_str_append_smart_str(&url_state->url_app, &svalue); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "<input type=\"hidden\" name=\""); - smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &sname); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" value=\""); - smart_str_append_smart_str(&BG(url_adapt_state_ex).form_app, &svalue); - smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" />"); + smart_str_appends(&url_state->form_app, "<input type=\"hidden\" name=\""); + smart_str_append_smart_str(&url_state->form_app, &hname); + smart_str_appends(&url_state->form_app, "\" value=\""); + smart_str_append_smart_str(&url_state->form_app, &hvalue); + smart_str_appends(&url_state->form_app, "\" />"); smart_str_free(&sname); smart_str_free(&svalue); + smart_str_free(&hname); + smart_str_free(&hvalue); return SUCCESS; } -PHPAPI int php_url_scanner_reset_vars(void) + +PHPAPI int php_url_scanner_add_session_var(char *name, size_t name_len, char *value, size_t value_len, int encode) +{ + return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 1); +} + + +PHPAPI int php_url_scanner_add_var(char *name, size_t name_len, char *value, size_t value_len, int encode) { - if (BG(url_adapt_state_ex).form_app.s) { - ZSTR_LEN(BG(url_adapt_state_ex).form_app.s) = 0; + return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 0); +} + + +static inline void php_url_scanner_reset_vars_impl(int type) { + url_adapt_state_ex_t *url_state; + + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); + } + + if (url_state->form_app.s) { + ZSTR_LEN(url_state->form_app.s) = 0; } - if (BG(url_adapt_state_ex).url_app.s) { - ZSTR_LEN(BG(url_adapt_state_ex).url_app.s) = 0; + if (url_state->url_app.s) { + ZSTR_LEN(url_state->url_app.s) = 0; } +} + +PHPAPI int php_url_scanner_reset_session_vars(void) +{ + php_url_scanner_reset_vars_impl(1); + return SUCCESS; +} + + +PHPAPI int php_url_scanner_reset_vars(void) +{ + php_url_scanner_reset_vars_impl(0); return SUCCESS; } + +static inline int php_url_scanner_reset_var_impl(zend_string *name, int encode, int type) +{ + char *start, *end, *limit; + size_t separator_len; + smart_str sname = {0}; + smart_str hname = {0}; + smart_str url_app = {0}; + smart_str form_app = {0}; + zend_string *encoded; + int ret = SUCCESS; + zend_bool sep_removed = 0; + url_adapt_state_ex_t *url_state; + + if (type) { + url_state = &BG(url_adapt_session_ex); + } else { + url_state = &BG(url_adapt_output_ex); + } + + /* Short circuit check. Only check url_app. */ + if (!url_state->url_app.s || !ZSTR_LEN(url_state->url_app.s)) { + return SUCCESS; + } + + if (encode) { + encoded = php_raw_url_encode(ZSTR_VAL(name), ZSTR_LEN(name)); + smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); + zend_string_free(encoded); + encoded = php_escape_html_entities_ex((unsigned char *)ZSTR_VAL(name), ZSTR_LEN(name), 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), 0); + smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); + zend_string_free(encoded); + } else { + smart_str_appendl(&sname, ZSTR_VAL(name), ZSTR_LEN(name)); + smart_str_appendl(&hname, ZSTR_VAL(name), ZSTR_LEN(name)); + } + smart_str_0(&sname); + smart_str_0(&hname); + + smart_str_append_smart_str(&url_app, &sname); + smart_str_appendc(&url_app, '='); + smart_str_0(&url_app); + + smart_str_appends(&form_app, "<input type=\"hidden\" name=\""); + smart_str_append_smart_str(&form_app, &hname); + smart_str_appends(&form_app, "\" value=\""); + smart_str_0(&form_app); + + /* Short circuit check. Only check url_app. */ + start = (char *) php_memnstr(ZSTR_VAL(url_state->url_app.s), + ZSTR_VAL(url_app.s), ZSTR_LEN(url_app.s), + ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s)); + if (!start) { + ret = FAILURE; + goto finish; + } + + /* Get end of url var */ + limit = ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s); + end = start + ZSTR_LEN(url_app.s); + separator_len = strlen(PG(arg_separator).output); + while (end < limit) { + if (!memcmp(end, PG(arg_separator).output, separator_len)) { + end += separator_len; + sep_removed = 1; + break; + } + end++; + } + /* Remove all when this is the only rewrite var */ + if (ZSTR_LEN(url_state->url_app.s) == end - start) { + php_url_scanner_reset_vars_impl(type); + goto finish; + } + /* Check preceeding separator */ + if (!sep_removed + && start - PG(arg_separator).output >= separator_len + && !memcmp(start - separator_len, PG(arg_separator).output, separator_len)) { + start -= separator_len; + } + /* Remove partially */ + memmove(start, end, + ZSTR_LEN(url_state->url_app.s) - (end - ZSTR_VAL(url_state->url_app.s))); + ZSTR_LEN(url_state->url_app.s) -= end - start; + ZSTR_VAL(url_state->url_app.s)[ZSTR_LEN(url_state->url_app.s)] = '\0'; + + /* Remove form var */ + start = (char *) php_memnstr(ZSTR_VAL(url_state->form_app.s), + ZSTR_VAL(form_app.s), ZSTR_LEN(form_app.s), + ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s)); + if (!start) { + /* Should not happen */ + ret = FAILURE; + php_url_scanner_reset_vars_impl(type); + goto finish; + } + /* Get end of form var */ + limit = ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s); + end = start + ZSTR_LEN(form_app.s); + while (end < limit) { + if (*end == '>') { + end += 1; + break; + } + end++; + } + /* Remove partially */ + memmove(start, end, + ZSTR_LEN(url_state->form_app.s) - (end - ZSTR_VAL(url_state->form_app.s))); + ZSTR_LEN(url_state->form_app.s) -= end - start; + ZSTR_VAL(url_state->form_app.s)[ZSTR_LEN(url_state->form_app.s)] = '\0'; + +finish: + smart_str_free(&url_app); + smart_str_free(&form_app); + smart_str_free(&sname); + smart_str_free(&hname); + return ret; +} + + +PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode) +{ + return php_url_scanner_reset_var_impl(name, encode, 1); +} + + +PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode) +{ + return php_url_scanner_reset_var_impl(name, encode, 0); +} + + PHP_MINIT_FUNCTION(url_scanner) { - BG(url_adapt_state_ex).tags = NULL; + BG(url_adapt_session_ex).tags = NULL; + BG(url_adapt_session_ex).form_app.s = BG(url_adapt_session_ex).url_app.s = NULL; + zend_hash_init(&BG(url_adapt_session_hosts_ht), 0, NULL, NULL, 1); + + BG(url_adapt_output_ex).tags = NULL; + BG(url_adapt_output_ex).form_app.s = BG(url_adapt_output_ex).url_app.s = NULL; + zend_hash_init(&BG(url_adapt_output_hosts_ht), 0, NULL, NULL, 1); - BG(url_adapt_state_ex).form_app.s = BG(url_adapt_state_ex).url_app.s = NULL; + BG(url_adapt_session_ex).type = 1; + BG(url_adapt_output_ex).type = 0; REGISTER_INI_ENTRIES(); return SUCCESS; @@ -567,20 +986,34 @@ PHP_MSHUTDOWN_FUNCTION(url_scanner) PHP_RINIT_FUNCTION(url_scanner) { - BG(url_adapt_state_ex).active = 0; - + BG(url_adapt_session_ex).active = 0; + BG(url_adapt_session_ex).tag_type = 0; + BG(url_adapt_session_ex).attr_type = 0; + BG(url_adapt_output_ex).active = 0; + BG(url_adapt_output_ex).tag_type = 0; + BG(url_adapt_output_ex).attr_type = 0; return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(url_scanner) { - if (BG(url_adapt_state_ex).active) { - php_url_scanner_ex_deactivate(); - BG(url_adapt_state_ex).active = 0; + if (BG(url_adapt_session_ex).active) { + php_url_scanner_ex_deactivate(1); + BG(url_adapt_session_ex).active = 0; + BG(url_adapt_session_ex).tag_type = 0; + BG(url_adapt_session_ex).attr_type = 0; } - - smart_str_free(&BG(url_adapt_state_ex).form_app); - smart_str_free(&BG(url_adapt_state_ex).url_app); + smart_str_free(&BG(url_adapt_session_ex).form_app); + smart_str_free(&BG(url_adapt_session_ex).url_app); + + if (BG(url_adapt_output_ex).active) { + php_url_scanner_ex_deactivate(0); + BG(url_adapt_output_ex).active = 0; + BG(url_adapt_output_ex).tag_type = 0; + BG(url_adapt_output_ex).attr_type = 0; + } + smart_str_free(&BG(url_adapt_output_ex).form_app); + smart_str_free(&BG(url_adapt_output_ex).url_app); return SUCCESS; } diff --git a/php.ini-development b/php.ini-development index b39689f5db..34a561851c 100644 --- a/php.ini-development +++ b/php.ini-development @@ -158,11 +158,6 @@ ; Development Value: On ; Production Value: Off -; url_rewriter.tags -; Default Value: "a=href,area=href,frame=src,form=,fieldset=" -; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" - ; variables_order ; Default Value: "EGPCS" ; Development Value: "GPCS" @@ -244,6 +239,23 @@ output_buffering = 4096 ; http://php.net/output-handler ;output_handler = +; URL rewriter function rewrites URL on the fly by using +; output buffer. You can set target tags by this configuration. +; "form" tag is special tag. It will add hidden input tag to pass values. +; Refer to session.trans_sid_tags for usage. +; Default Value: "form=" +; Development Value: "form=" +; Production Value: "form=" +;url_rewriter.tags + +; URL rewriter will not rewrites absolute URL nor form by default. To enable +; absolute URL rewrite, allowed hosts must be defined at RUNTIME. +; Refer to session.trans_sid_hosts for more details. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;url_rewriter.hosts + ; Transparent output compression using the zlib library ; Valid values for this option are 'off', 'on', or a specific buffer size ; to be used for compression (default is 4KB) @@ -1437,6 +1449,31 @@ session.cache_expire = 180 ; http://php.net/session.use-trans-sid session.use_trans_sid = 0 +; The URL rewriter will look for URLs in a defined set of HTML tags. +; <form> is special; if you include them here, the rewriter will +; add a hidden <input> field with the info which is otherwise appended +; to URLs. <form> tag's action attribute URL will not be modified +; unless it is specified. +; Note that all valid entries require a "=", even if no value follows. +; Default Value: "a=href,area=href,frame=src,form=" +; Development Value: "a=href,area=href,frame=src,form=" +; Production Value: "a=href,area=href,frame=src,form=" +; http://php.net/url-rewriter.tags +session.trans_sid_tags = "a=href,area=href,frame=src,form=" + +; URL rewriter does not rewrite absolute URLs by default. +; To enable rewrites for absolute pathes, target hosts must be specified +; at RUNTIME. i.e. use ini_set() +; <form> tags is special. PHP will check action attribute's URL regardless +; of session.trans_sid_tags setting. +; If no host is defined, HTTP_HOST will be used for allowed host. +; Example value: php.net,www.php.net,wiki.php.net +; Use "," for multiple hosts. No spaces are allowed. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;session.trans_sid_hosts="" + ; Select a hash function for use in generating session ids. ; Possible Values ; 0 (MD5 128 bits) @@ -1459,17 +1496,6 @@ session.hash_function = 0 ; http://php.net/session.hash-bits-per-character session.hash_bits_per_character = 5 -; The URL rewriter will look for URLs in a defined set of HTML tags. -; form/fieldset are special; if you include them here, the rewriter will -; add a hidden <input> field with the info which is otherwise appended -; to URLs. If you want XHTML conformity, remove the form entry. -; Note that all valid entries require a "=", even if no value follows. -; Default Value: "a=href,area=href,frame=src,form=,fieldset=" -; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; http://php.net/url-rewriter.tags -url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry" - ; Enable upload progress tracking in $_SESSION ; Default Value: On ; Development Value: On diff --git a/php.ini-production b/php.ini-production index 3c07f752dd..e95b1cfb70 100644 --- a/php.ini-production +++ b/php.ini-production @@ -158,11 +158,6 @@ ; Development Value: On ; Production Value: Off -; url_rewriter.tags -; Default Value: "a=href,area=href,frame=src,form=,fieldset=" -; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" - ; variables_order ; Default Value: "EGPCS" ; Development Value: "GPCS" @@ -244,6 +239,23 @@ output_buffering = 4096 ; http://php.net/output-handler ;output_handler = +; URL rewriter function rewrites URL on the fly by using +; output buffer. You can set target tags by this configuration. +; "form" tag is special tag. It will add hidden input tag to pass values. +; Refer to session.trans_sid_tags for usage. +; Default Value: "form=" +; Development Value: "form=" +; Production Value: "form=" +;url_rewriter.tags + +; URL rewriter will not rewrites absolute URL nor form by default. To enable +; absolute URL rewrite, allowed hosts must be defined at RUNTIME. +; Refer to session.trans_sid_hosts for more details. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;url_rewriter.hosts + ; Transparent output compression using the zlib library ; Valid values for this option are 'off', 'on', or a specific buffer size ; to be used for compression (default is 4KB) @@ -1437,6 +1449,31 @@ session.cache_expire = 180 ; http://php.net/session.use-trans-sid session.use_trans_sid = 0 +; The URL rewriter will look for URLs in a defined set of HTML tags. +; <form> is special; if you include them here, the rewriter will +; add a hidden <input> field with the info which is otherwise appended +; to URLs. <form> tag's action attribute URL will not be modified +; unless it is specified. +; Note that all valid entries require a "=", even if no value follows. +; Default Value: "a=href,area=href,frame=src,form=" +; Development Value: "a=href,area=href,frame=src,form=" +; Production Value: "a=href,area=href,frame=src,form=" +; http://php.net/url-rewriter.tags +session.trans_sid_tags = "a=href,area=href,frame=src,form=" + +; URL rewriter does not rewrite absolute URLs by default. +; To enable rewrites for absolute pathes, target hosts must be specified +; at RUNTIME. i.e. use ini_set() +; <form> tags is special. PHP will check action attribute's URL regardless +; of session.trans_sid_tags setting. +; If no host is defined, HTTP_HOST will be used for allowed host. +; Example value: php.net,www.php.net,wiki.php.net +; Use "," for multiple hosts. No spaces are allowed. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;session.trans_sid_hosts="" + ; Select a hash function for use in generating session ids. ; Possible Values ; 0 (MD5 128 bits) @@ -1459,17 +1496,6 @@ session.hash_function = 0 ; http://php.net/session.hash-bits-per-character session.hash_bits_per_character = 5 -; The URL rewriter will look for URLs in a defined set of HTML tags. -; form/fieldset are special; if you include them here, the rewriter will -; add a hidden <input> field with the info which is otherwise appended -; to URLs. If you want XHTML conformity, remove the form entry. -; Note that all valid entries require a "=", even if no value follows. -; Default Value: "a=href,area=href,frame=src,form=,fieldset=" -; Development Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; Production Value: "a=href,area=href,frame=src,input=src,form=fakeentry" -; http://php.net/url-rewriter.tags -url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry" - ; Enable upload progress tracking in $_SESSION ; Default Value: On ; Development Value: On |