<?php
class Ext_Sandbox_PHPValidator
{
public static function php_syntax_error($code, $tokens = null)
{
$braces = 0;
$inString = 0;
$isCodeHtml = false;
if (!$tokens) {
}
// First of all, we need to know if braces are correctly balanced.
// This is not trivial due to variable interpolation which
// occurs in heredoc, backticked and double quoted strings
foreach ($tokens as $token) {
switch ($token[0]) {
case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case T_START_HEREDOC:
++$inString;
break;
case T_END_HEREDOC:
--$inString;
break;
case T_OPEN_TAG:
$isCodeHtml = false;
break;
case T_CLOSE_TAG:
$isCodeHtml = true;
break;
}
} else if ($inString & 1) {
switch ($token) {
case '`':
case '"':
--$inString;
break;
}
} else {
switch ($token) {
case '`':
case '"':
++$inString;
break;
case '{':
++$braces;
break;
case '}':
if ($inString) --$inString;
else {
--$braces;
if ($braces < 0) break 2;
}
break;
}
}
}
// Display parse error messages and use output buffering to catch them
$inString = @ini_set('log_errors', false); $token = @ini_set('display_errors', true);
// If $braces is not zero, then we are sure that $code is broken.
// We run it anyway in order to catch the error message and line number.
// Else, if $braces are correctly balanced, then we can safely put
// $code in a dead code sandbox to prevent its execution.
// Note that without this sandbox, a function or class declaration inside
// $code could throw a "Cannot redeclare" fatal error.
$braces || $code = "if(0){?><?php {$code}\n" . ($isCodeHtml ? '<?php }' : '}');
if (false === eval($code)) { if ($braces) $braces = PHP_INT_MAX;
else {
// Get the maximum number of lines in $code to fix a border case
}
// Get the error message and line number
if (preg_match("/syntax error, (.+) in .+ on line (\\d+)$/s", $code, $code)) { $code[2] = (int)$code[2];
$code = $code[2] <= $braces
?
array($code[1], $code[2]) : array('unexpected $end ' . substr($code[1], 14), $braces); } else $code = array('syntax error', 0); } else {
$code = false;
}
@ini_set('display_errors', $token);
return $code;
}
public static
function validatePHPCode
($source, $functions = array(), $enable = true) {
$inner_functions = array();
$func_started = 0;
$previousToken = null;
if ($error = self::php_syntax_error($source, $tokens)) {
throw new Exception($error[0] . ': ' . $error[1]);
}
$previousTokenContent = '';
foreach ($tokens as $token) {
if ($token == '(') {
if ($func_started) {
$func_started = 0;
} else {
if ($previousToken[0] == T_STRING) {
$funcSearch = implode('::', $callStack); if (strlen($funcSearch) && $funcSearch[0] != '$') { if (!count($callStack) || (!$enable ^
(false === array_search($funcSearch, $functions)))) { if (!in_array($funcSearch, $inner_functions)) { throw new Exception('Function is disabled: ' . $funcSearch);
}
}
}
}
if (in_array($previousToken[0], array(T_VARIABLE
, T_STRING_VARNAME
, T_ENCAPSED_AND_WHITESPACE
))) { if (!in_array($previousTokenContent, array('+', '-', '*', '/', '.', '^', '&', '?', '!', '%', '@'))) { throw new Exception('Only direct function calls allowed, line ' . $previousToken[2]);
}
}
}
}
if ($token != '.') {
}
} else {
list($id, $text) = $token;
if (in_array($id, array(T_COMMENT
, T_DOC_COMMENT
, T_WHITESPACE
))) { continue;
}
switch ($id) {
case T_FUNCTION:
$func_started = 1;
break;
case T_STRING:
$callStack[] = $text;
if ($func_started) {
$inner_functions[] = $text;
}
break;
case T_NS_SEPARATOR:
case T_DOUBLE_COLON:
case T_OBJECT_OPERATOR:
break;
case T_VARIABLE:
$callStack[] = $token[1];
break;
case T_EVAL:
if (!$enable ^
(false === array_search('eval', $functions))) { throw new Exception('Eval is disabled, line ' . $token[2]);
}
break;
case T_PRINT:
if (!$enable ^
(false === array_search('print', $functions))) { throw new Exception('Print is disabled, line ' . $token[2]);
}
break;
case T_ECHO:
if (!$enable ^
(false === array_search('echo', $functions))) { throw new Exception('Echo is disabled, line ' . $token[2]);
}
break;
case T_EXIT:
throw new Exception('Exit/Die is disabled, line ' . $token[2]);
}
break;
case T_THROW:
if (!$enable ^
(false === array_search('throw', $functions))) { throw new Exception('Throw is disabled, line ' . $token[2]);
}
break;
default:
break;
}
}
$previousToken = $token;
}
return true;
}
}
##################################
$code = <<<PHP
\$b = 1;
\$c = 2;
\$a = \$b + \$c;
echo \$a;
class test {
public function __construct() {
echo 'construct';
}
public function foo(\$num) {
var_dump(\$num);
}
}
\$test = new test();
\$test->foo(\$a);
PHP;
// validate the code
$validator = new Ext_Sandbox_PHPValidator();
try
{
// we enable only one function - echo, all others will throw error
$validator->validatePHPCode( $code, array('echo'), true); $status = 'passed';
}
catch(Exception $ex)
{
$status = $ex->getMessage();
}
echo 'Status of validation is: ' . $status;
PD9waHAKCmNsYXNzIEV4dF9TYW5kYm94X1BIUFZhbGlkYXRvcgp7CiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIHBocF9zeW50YXhfZXJyb3IoJGNvZGUsICR0b2tlbnMgPSBudWxsKQogICAgewogICAgICAgICRicmFjZXMgPSAwOwogICAgICAgICRpblN0cmluZyA9IDA7CgogICAgICAgICRpc0NvZGVIdG1sID0gZmFsc2U7CgogICAgICAgIGlmICghJHRva2VucykgewogICAgICAgICAgICAkdG9rZW5zID0gdG9rZW5fZ2V0X2FsbCgnPD9waHAgJyAuICRjb2RlKTsKICAgICAgICB9CgogICAgICAgIC8vIEZpcnN0IG9mIGFsbCwgd2UgbmVlZCB0byBrbm93IGlmIGJyYWNlcyBhcmUgY29ycmVjdGx5IGJhbGFuY2VkLgogICAgICAgIC8vIFRoaXMgaXMgbm90IHRyaXZpYWwgZHVlIHRvIHZhcmlhYmxlIGludGVycG9sYXRpb24gd2hpY2gKICAgICAgICAvLyBvY2N1cnMgaW4gaGVyZWRvYywgYmFja3RpY2tlZCBhbmQgZG91YmxlIHF1b3RlZCBzdHJpbmdzCiAgICAgICAgZm9yZWFjaCAoJHRva2VucyBhcyAkdG9rZW4pIHsKICAgICAgICAgICAgaWYgKGlzX2FycmF5KCR0b2tlbikpIHsKICAgICAgICAgICAgICAgIHN3aXRjaCAoJHRva2VuWzBdKSB7CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX0NVUkxZX09QRU46CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX0RPTExBUl9PUEVOX0NVUkxZX0JSQUNFUzoKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfU1RBUlRfSEVSRURPQzoKICAgICAgICAgICAgICAgICAgICAgICAgKyskaW5TdHJpbmc7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgICAgIGNhc2UgVF9FTkRfSEVSRURPQzoKICAgICAgICAgICAgICAgICAgICAgICAgLS0kaW5TdHJpbmc7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwoKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfT1BFTl9UQUc6CiAgICAgICAgICAgICAgICAgICAgICAgICRpc0NvZGVIdG1sID0gZmFsc2U7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgICAgIGNhc2UgVF9DTE9TRV9UQUc6CiAgICAgICAgICAgICAgICAgICAgICAgICRpc0NvZGVIdG1sID0gdHJ1ZTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gZWxzZSBpZiAoJGluU3RyaW5nICYgMSkgewogICAgICAgICAgICAgICAgc3dpdGNoICgkdG9rZW4pIHsKICAgICAgICAgICAgICAgICAgICBjYXNlICdgJzoKICAgICAgICAgICAgICAgICAgICBjYXNlICciJzoKICAgICAgICAgICAgICAgICAgICAgICAgLS0kaW5TdHJpbmc7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgc3dpdGNoICgkdG9rZW4pIHsKICAgICAgICAgICAgICAgICAgICBjYXNlICdgJzoKICAgICAgICAgICAgICAgICAgICBjYXNlICciJzoKICAgICAgICAgICAgICAgICAgICAgICAgKyskaW5TdHJpbmc7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwoKICAgICAgICAgICAgICAgICAgICBjYXNlICd7JzoKICAgICAgICAgICAgICAgICAgICAgICAgKyskYnJhY2VzOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlICd9JzoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRpblN0cmluZykgLS0kaW5TdHJpbmc7CiAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0kYnJhY2VzOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCRicmFjZXMgPCAwKSBicmVhayAyOwogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgLy8gRGlzcGxheSBwYXJzZSBlcnJvciBtZXNzYWdlcyBhbmQgdXNlIG91dHB1dCBidWZmZXJpbmcgdG8gY2F0Y2ggdGhlbQogICAgICAgICRpblN0cmluZyA9IEBpbmlfc2V0KCdsb2dfZXJyb3JzJywgZmFsc2UpOwogICAgICAgICR0b2tlbiA9IEBpbmlfc2V0KCdkaXNwbGF5X2Vycm9ycycsIHRydWUpOwogICAgICAgIG9iX3N0YXJ0KCk7CgogICAgICAgIC8vIElmICRicmFjZXMgaXMgbm90IHplcm8sIHRoZW4gd2UgYXJlIHN1cmUgdGhhdCAkY29kZSBpcyBicm9rZW4uCiAgICAgICAgLy8gV2UgcnVuIGl0IGFueXdheSBpbiBvcmRlciB0byBjYXRjaCB0aGUgZXJyb3IgbWVzc2FnZSBhbmQgbGluZSBudW1iZXIuCgogICAgICAgIC8vIEVsc2UsIGlmICRicmFjZXMgYXJlIGNvcnJlY3RseSBiYWxhbmNlZCwgdGhlbiB3ZSBjYW4gc2FmZWx5IHB1dAogICAgICAgIC8vICRjb2RlIGluIGEgZGVhZCBjb2RlIHNhbmRib3ggdG8gcHJldmVudCBpdHMgZXhlY3V0aW9uLgogICAgICAgIC8vIE5vdGUgdGhhdCB3aXRob3V0IHRoaXMgc2FuZGJveCwgYSBmdW5jdGlvbiBvciBjbGFzcyBkZWNsYXJhdGlvbiBpbnNpZGUKICAgICAgICAvLyAkY29kZSBjb3VsZCB0aHJvdyBhICJDYW5ub3QgcmVkZWNsYXJlIiBmYXRhbCBlcnJvci4KCiAgICAgICAgJGJyYWNlcyB8fCAkY29kZSA9ICJpZigwKXs/Pjw/cGhwIHskY29kZX1cbiIgLiAoJGlzQ29kZUh0bWwgPyAnPD9waHAgfScgOiAnfScpOwoKICAgICAgICBpZiAoZmFsc2UgPT09IGV2YWwoJGNvZGUpKSB7CiAgICAgICAgICAgIGlmICgkYnJhY2VzKSAkYnJhY2VzID0gUEhQX0lOVF9NQVg7CiAgICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBtYXhpbXVtIG51bWJlciBvZiBsaW5lcyBpbiAkY29kZSB0byBmaXggYSBib3JkZXIgY2FzZQogICAgICAgICAgICAgICAgZmFsc2UgIT09IHN0cnBvcygkY29kZSwgIlxyIikgJiYgJGNvZGUgPSBzdHJ0cihzdHJfcmVwbGFjZSgiXHJcbiIsICJcbiIsICRjb2RlKSwgIlxyIiwgIlxuIik7CiAgICAgICAgICAgICAgICAkYnJhY2VzID0gc3Vic3RyX2NvdW50KCRjb2RlLCAiXG4iKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgJGNvZGUgPSBvYl9nZXRfY2xlYW4oKTsKICAgICAgICAgICAgJGNvZGUgPSBzdHJpcF90YWdzKCRjb2RlKTsKCiAgICAgICAgICAgIC8vIEdldCB0aGUgZXJyb3IgbWVzc2FnZSBhbmQgbGluZSBudW1iZXIKICAgICAgICAgICAgaWYgKHByZWdfbWF0Y2goIi9zeW50YXggZXJyb3IsICguKykgaW4gLisgb24gbGluZSAoXFxkKykkL3MiLCAkY29kZSwgJGNvZGUpKSB7CiAgICAgICAgICAgICAgICAkY29kZVsyXSA9IChpbnQpJGNvZGVbMl07CiAgICAgICAgICAgICAgICAkY29kZSA9ICRjb2RlWzJdIDw9ICRicmFjZXMKICAgICAgICAgICAgICAgICAgICA/IGFycmF5KCRjb2RlWzFdLCAkY29kZVsyXSkKICAgICAgICAgICAgICAgICAgICA6IGFycmF5KCd1bmV4cGVjdGVkICRlbmQgJyAuIHN1YnN0cigkY29kZVsxXSwgMTQpLCAkYnJhY2VzKTsKICAgICAgICAgICAgfSBlbHNlICRjb2RlID0gYXJyYXkoJ3N5bnRheCBlcnJvcicsIDApOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIG9iX2VuZF9jbGVhbigpOwogICAgICAgICAgICAkY29kZSA9IGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgQGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywgJHRva2VuKTsKICAgICAgICBAaW5pX3NldCgnbG9nX2Vycm9ycycsICRpblN0cmluZyk7CgogICAgICAgIHJldHVybiAkY29kZTsKICAgIH0KCiAgICBwdWJsaWMgc3RhdGljIGZ1bmN0aW9uIHZhbGlkYXRlUEhQQ29kZSgkc291cmNlLCAkZnVuY3Rpb25zID0gYXJyYXkoKSwgJGVuYWJsZSA9IHRydWUpCiAgICB7CiAgICAgICAgJGlubmVyX2Z1bmN0aW9ucyA9IGFycmF5KCk7CgogICAgICAgICRmdW5jX3N0YXJ0ZWQgPSAwOwoKICAgICAgICAkcHJldmlvdXNUb2tlbiA9IG51bGw7CgogICAgICAgICRjYWxsU3RhY2sgPSBhcnJheSgpOwoKICAgICAgICAkdG9rZW5zID0gdG9rZW5fZ2V0X2FsbCgnPD9waHAgJyAuICRzb3VyY2UpOwoKICAgICAgICBpZiAoJGVycm9yID0gc2VsZjo6cGhwX3N5bnRheF9lcnJvcigkc291cmNlLCAkdG9rZW5zKSkgewogICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCRlcnJvclswXSAuICc6ICcgLiAkZXJyb3JbMV0pOwogICAgICAgIH0KCiAgICAgICAgJHByZXZpb3VzVG9rZW5Db250ZW50ID0gJyc7CgogICAgICAgIGZvcmVhY2ggKCR0b2tlbnMgYXMgJHRva2VuKSB7CiAgICAgICAgICAgIGlmIChpc19zdHJpbmcoJHRva2VuKSkgewogICAgICAgICAgICAgICAgaWYgKCR0b2tlbiA9PSAnKCcpIHsKICAgICAgICAgICAgICAgICAgICBpZiAoJGZ1bmNfc3RhcnRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICAkZnVuY19zdGFydGVkID0gMDsKICAgICAgICAgICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgICAgICAgICAgICBpZiAoJHByZXZpb3VzVG9rZW5bMF0gPT0gVF9TVFJJTkcpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICRmdW5jU2VhcmNoID0gaW1wbG9kZSgnOjonLCAkY2FsbFN0YWNrKTsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzdHJsZW4oJGZ1bmNTZWFyY2gpICYmICRmdW5jU2VhcmNoWzBdICE9ICckJykgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghY291bnQoJGNhbGxTdGFjaykgfHwgKCEkZW5hYmxlIF4gKGZhbHNlID09PSBhcnJheV9zZWFyY2goJGZ1bmNTZWFyY2gsICRmdW5jdGlvbnMpKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpbl9hcnJheSgkZnVuY1NlYXJjaCwgJGlubmVyX2Z1bmN0aW9ucykpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJ0Z1bmN0aW9uIGlzIGRpc2FibGVkOiAnIC4gJGZ1bmNTZWFyY2gpOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoaW5fYXJyYXkoJHByZXZpb3VzVG9rZW5bMF0sIGFycmF5KFRfVkFSSUFCTEUsIFRfU1RSSU5HX1ZBUk5BTUUsIFRfRU5DQVBTRURfQU5EX1dISVRFU1BBQ0UpKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFpbl9hcnJheSgkcHJldmlvdXNUb2tlbkNvbnRlbnQsIGFycmF5KCcrJywgJy0nLCAnKicsICcvJywgJy4nLCAnXicsICcmJywgJz8nLCAnIScsICclJywgJ0AnKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCdPbmx5IGRpcmVjdCBmdW5jdGlvbiBjYWxscyBhbGxvd2VkLCBsaW5lICcgLiAkcHJldmlvdXNUb2tlblsyXSk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CgogICAgICAgICAgICAgICAgaWYgKCR0b2tlbiAhPSAnLicpIHsKICAgICAgICAgICAgICAgICAgICAkY2FsbFN0YWNrID0gYXJyYXkoKTsKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIGxpc3QoJGlkLCAkdGV4dCkgPSAkdG9rZW47CgogICAgICAgICAgICAgICAgaWYgKGluX2FycmF5KCRpZCwgYXJyYXkoVF9DT01NRU5ULCBUX0RPQ19DT01NRU5ULCBUX1dISVRFU1BBQ0UpKSkgewogICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgICAgfQoKICAgICAgICAgICAgICAgIHN3aXRjaCAoJGlkKSB7CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX0ZVTkNUSU9OOgogICAgICAgICAgICAgICAgICAgICAgICAkZnVuY19zdGFydGVkID0gMTsKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX1NUUklORzoKICAgICAgICAgICAgICAgICAgICAgICAgJGNhbGxTdGFja1tdID0gJHRleHQ7CgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoJGZ1bmNfc3RhcnRlZCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgJGlubmVyX2Z1bmN0aW9uc1tdID0gJHRleHQ7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX05TX1NFUEFSQVRPUjoKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfRE9VQkxFX0NPTE9OOgogICAgICAgICAgICAgICAgICAgIGNhc2UgVF9PQkpFQ1RfT1BFUkFUT1I6CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgICAgIGNhc2UgVF9WQVJJQUJMRToKICAgICAgICAgICAgICAgICAgICAgICAgJGNhbGxTdGFja1tdID0gJHRva2VuWzFdOwogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfRVZBTDoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCEkZW5hYmxlIF4gKGZhbHNlID09PSBhcnJheV9zZWFyY2goJ2V2YWwnLCAkZnVuY3Rpb25zKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJ0V2YWwgaXMgZGlzYWJsZWQsIGxpbmUgJyAuICR0b2tlblsyXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgY2FzZSBUX1BSSU5UOgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoISRlbmFibGUgXiAoZmFsc2UgPT09IGFycmF5X3NlYXJjaCgncHJpbnQnLCAkZnVuY3Rpb25zKSkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeGNlcHRpb24oJ1ByaW50IGlzIGRpc2FibGVkLCBsaW5lICcgLiAkdG9rZW5bMl0pOwogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgICAgIGNhc2UgVF9FQ0hPOgogICAgICAgICAgICAgICAgICAgICAgICBpZiAoISRlbmFibGUgXiAoZmFsc2UgPT09IGFycmF5X3NlYXJjaCgnZWNobycsICRmdW5jdGlvbnMpKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbignRWNobyBpcyBkaXNhYmxlZCwgbGluZSAnIC4gJHRva2VuWzJdKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfRVhJVDoKICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCEkZW5hYmxlIF4gKChmYWxzZSA9PT0gYXJyYXlfc2VhcmNoKCdkaWUnLCAkZnVuY3Rpb25zKSkgJiYgKGZhbHNlID09PSBhcnJheV9zZWFyY2goJ2V4aXQnLCAkZnVuY3Rpb25zKSkpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhjZXB0aW9uKCdFeGl0L0RpZSBpcyBkaXNhYmxlZCwgbGluZSAnIC4gJHRva2VuWzJdKTsKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgICAgICAgICBjYXNlIFRfVEhST1c6CiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghJGVuYWJsZSBeIChmYWxzZSA9PT0gYXJyYXlfc2VhcmNoKCd0aHJvdycsICRmdW5jdGlvbnMpKSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4Y2VwdGlvbignVGhyb3cgaXMgZGlzYWJsZWQsIGxpbmUgJyAuICR0b2tlblsyXSk7CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgICAgICAgICAgICAgJGNhbGxTdGFjayA9IGFycmF5KCk7CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICAkcHJldmlvdXNUb2tlbiA9ICR0b2tlbjsKICAgICAgICB9CgogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9CgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiRjb2RlID0gPDw8UEhQClwkYiA9IDE7ClwkYyA9IDI7ClwkYSA9IFwkYiArIFwkYzsKZWNobyBcJGE7CgpjbGFzcyB0ZXN0IHsKICAgIHB1YmxpYyBmdW5jdGlvbiBfX2NvbnN0cnVjdCgpIHsKICAgICAgICBlY2hvICdjb25zdHJ1Y3QnOwogICAgfQogICAgcHVibGljIGZ1bmN0aW9uIGZvbyhcJG51bSkgewogICAgICAgIHZhcl9kdW1wKFwkbnVtKTsKICAgIH0KfQoKXCR0ZXN0ID0gbmV3IHRlc3QoKTsKXCR0ZXN0LT5mb28oXCRhKTsKUEhQOwoKLy8gdmFsaWRhdGUgdGhlIGNvZGUKJHZhbGlkYXRvciA9IG5ldyBFeHRfU2FuZGJveF9QSFBWYWxpZGF0b3IoKTsKCnRyeQp7CiAgICAvLyB3ZSBlbmFibGUgb25seSBvbmUgZnVuY3Rpb24gLSBlY2hvLCBhbGwgb3RoZXJzIHdpbGwgdGhyb3cgZXJyb3IKICAgICR2YWxpZGF0b3ItPnZhbGlkYXRlUEhQQ29kZSggJGNvZGUsIGFycmF5KCdlY2hvJyksIHRydWUpOwogICAgJHN0YXR1cyA9ICdwYXNzZWQnOwp9CmNhdGNoKEV4Y2VwdGlvbiAkZXgpCnsKICAgICRzdGF0dXMgPSAkZXgtPmdldE1lc3NhZ2UoKTsKfQoKZWNobyAnU3RhdHVzIG9mIHZhbGlkYXRpb24gaXM6ICcgLiAkc3RhdHVzOw==