Index: /branches/1.3/ChangeLog
===================================================================
--- /branches/1.3/ChangeLog	(revision 783)
+++ /branches/1.3/ChangeLog	(revision 784)
@@ -1,4 +1,5 @@
 1.3.2 2011-??-??
 ========
+ * avoid possible filename injection in admin page
  * adds 30 seconds timeout to "compiling" flag
  * decompiler: improves decompiling
Index: /branches/1.3/Decompiler.class.php
===================================================================
--- /branches/1.3/Decompiler.class.php	(revision 783)
+++ /branches/1.3/Decompiler.class.php	(revision 784)
@@ -737,4 +737,6 @@
 		$EX['last'] = count($opcodes) - 1;
 		$EX['silence'] = 0;
+		$EX['recvs'] = array();
+		$EX['uses'] = array();
 
 		for ($next = 0, $last = $EX['last'];
@@ -1139,4 +1141,19 @@
 						break;
 					}
+					if (is_a($rvalue, 'Decompiler_Fetch')) {
+						$src = str($rvalue->src, $EX);
+						if ('$' . unquoteName($src) == $lvalue) {
+							switch ($rvalue->fetchType) {
+							case ZEND_FETCH_STATIC:
+								$statics = &$EX['op_array']['static_variables'];
+								if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
+									$EX['uses'][] = str($lvalue);
+									unset($statics);
+									break 2;
+								}
+								unset($statics);
+							}
+						}
+					}
 					$resvar = "$lvalue = " . str($rvalue, $EX);
 					break;
@@ -1155,4 +1172,10 @@
 							case ZEND_FETCH_STATIC:
 								$statics = &$EX['op_array']['static_variables'];
+								if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
+									$EX['uses'][] = '&' . str($lvalue);
+									unset($statics);
+									break 2;
+								}
+
 								$resvar = 'static ' . $lvalue;
 								$name = unquoteName($src);
@@ -1203,4 +1226,8 @@
 					if ($opc == XC_ISSET_ISEMPTY_VAR) {
 						$rvalue = $this->getOpVal($op1, $EX);
+						// for < PHP_5_3
+						if ($op1['op_type'] == XC_IS_CONST) {
+							$rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX));
+						}
 						if ($op2['EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
 							$class = $this->getOpVal($op2, $EX);
@@ -1227,8 +1254,8 @@
 					switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) {
 					case ZEND_ISSET:
-						$rvalue = "isset($rvalue)";
+						$rvalue = "isset(" . str($rvalue) . ")";
 						break;
 					case ZEND_ISEMPTY:
-						$rvalue = "empty($rvalue)";
+						$rvalue = "empty(" . str($rvalue) . ")";
 						break;
 					}
@@ -1838,4 +1865,11 @@
 	}
 	// }}}
+	function duses(&$EX, $indent) // {{{
+	{
+		if ($EX['uses']) {
+			echo ' use(', implode(', ', $EX['uses']), ')';
+		}
+	}
+	// }}}
 	function dfunction($func, $indent = '', $nobody = false) // {{{
 	{
@@ -1846,4 +1880,5 @@
 			$EX['op_array'] = &$func['op_array'];
 			$EX['recvs'] = array();
+			$EX['uses'] = array();
 		}
 		else {
@@ -1852,7 +1887,4 @@
 			$EX = &$this->dop_array($func['op_array'], $newindent);
 			$body = ob_get_clean();
-			if (!isset($EX['recvs'])) {
-				$EX['recvs'] = array();
-			}
 		}
 
@@ -1861,7 +1893,8 @@
 			$functionName = '';
 		}
-		echo 'function ', $functionName, '(';
+		echo 'function', $functionName ? ' ' . $functionName : '', '(';
 		$this->dargs($EX, $indent);
 		echo ")";
+		$this->duses($EX, $indent);
 		if ($nobody) {
 			echo ";\n";
@@ -2231,4 +2264,10 @@
 define('IS_CONSTANT', 8);
 define('IS_CONSTANT_ARRAY',   9);
+/* Ugly hack to support constants as static array indices */
+define('IS_CONSTANT_TYPE_MASK',   0x0f);
+define('IS_CONSTANT_UNQUALIFIED', 0x10);
+define('IS_CONSTANT_INDEX',       0x80);
+define('IS_LEXICAL_VAR',          0x20);
+define('IS_LEXICAL_REF',          0x40);
 
 @define('XC_IS_CV', 16);
Index: /branches/1.3/NEWS
===================================================================
--- /branches/1.3/NEWS	(revision 783)
+++ /branches/1.3/NEWS	(revision 784)
@@ -1,4 +1,5 @@
 1.3.2 2011-??-??
 ========
+ * admin page security fix
  * adds 30 seconds timeout to "compiling" flag
  * improves decompiling
Index: /branches/1.3/admin/common.php
===================================================================
--- /branches/1.3/admin/common.php	(revision 783)
+++ /branches/1.3/admin/common.php	(revision 784)
@@ -1,3 +1,8 @@
 <?php
+
+function xcache_validateFileName($name)
+{
+	return preg_match('!^[a-zA-Z0-9._-]+$!', $name);
+}
 
 function get_language_file_ex($name, $l, $s)
@@ -16,14 +21,17 @@
 		$l = $lmap[$l];
 	}
-	if (file_exists($file = "$name-$l-$s.lang.php")) {
+	$file = "$name-$l-$s.lang.php";
+	if (xcache_validateFileName($file) && file_exists($file)) {
 		return $file;
 	}
 	if (isset($smap[$s])) {
 		$s = $smap[$s];
-		if (file_exists($file = "$name-$l-$s.lang.php")) {
+		$file = "$name-$l-$s.lang.php";
+		if (xcache_validateFileName($file) && file_exists($file)) {
 			return $file;
 		}
 	}
-	if (file_exists($file = "$name-$l.lang.php")) {
+	$file = "$name-$l.lang.php";
+	if (xcache_validateFileName($file) && file_exists($file)) {
 		return $file;
 	}
@@ -39,5 +47,5 @@
 		$file = get_language_file_ex($name, $l, $s);
 		if (!isset($file)) {
-			$l = strtok($l, '-');
+			$l = strtok($l, ':-');
 			$file = get_language_file_ex($name, $l, $s);
 		}
@@ -45,5 +53,5 @@
 	else if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
 		foreach (explode(',', str_replace(' ', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'])) as $l) {
-			$l = strtok($l, ';');
+			$l = strtok($l, ':;');
 			$file = get_language_file_ex($name, $l, $s);
 			if (isset($file)) {
@@ -52,5 +60,5 @@
 			}
 			if (strpos($l, '-') !== false) {
-				$ll = strtok($l, '-');
+				$ll = strtok($l, ':-');
 				$file = get_language_file_ex($name, $ll, $s);
 				if (isset($file)) {
Index: /branches/1.3/decompilesample.php
===================================================================
--- /branches/1.3/decompilesample.php	(revision 783)
+++ /branches/1.3/decompilesample.php	(revision 784)
@@ -360,7 +360,10 @@
 $total = 0;
 $tax = 1;
-$callback = function ($quantity, $product) use ($tax, &$total) {
-	static $static = array(1);
+$callback = function($quantity, $product) use($tax, &$total) {
 	$tax = 'tax';
+	static $static1 = array(1);
+	static $static2;
+	$tax = 'tax';
+	$tax = --$tax;
 	$pricePerItem = constant('PRICE_' . strtoupper($product));
 	$total += $pricePerItem * $quantity * ($tax + 1);
Index: /branches/1.3/opcode_spec_def.h
===================================================================
--- /branches/1.3/opcode_spec_def.h	(revision 783)
+++ /branches/1.3/opcode_spec_def.h	(revision 784)
@@ -147,5 +147,7 @@
 	OPSPEC(    UNUSED,      VAR_2,        STD,        VAR) /* 97 FETCH_OBJ_UNSET                */
 	OPSPEC(    UNUSED,        STD,        STD,        VAR) /* 98 FETCH_DIM_TMP_VAR              */
-#ifdef ZEND_ENGINE_2
+#ifdef ZEND_ENGINE_2_3
+	OPSPEC(    UNUSED,      VAR_2,        STD,        TMP) /* 99 FETCH_CONSTANT                 */
+#elif defined(ZEND_ENGINE_2)
 	OPSPEC(    UNUSED,     UCLASS,        STD,        TMP) /* 99 FETCH_CONSTANT                 */
 #else
Index: /branches/1.3/utils.c
===================================================================
--- /branches/1.3/utils.c	(revision 783)
+++ /branches/1.3/utils.c	(revision 784)
@@ -244,10 +244,4 @@
 			case IS_TMP_VAR:
 				break;
-
-			case IS_CONST:
-				if (spec == OPSPEC_UCLASS) {
-					break;
-				}
-				/* fall */
 
 			default:
Index: /branches/1.3/xcache.c
===================================================================
--- /branches/1.3/xcache.c	(revision 783)
+++ /branches/1.3/xcache.c	(revision 784)
@@ -2280,4 +2280,32 @@
 }
 /* }}} */
+/* {{{ proto int xcache_get_refcount(mixed variable)
+   XCache internal uses only: Get reference count of variable */
+PHP_FUNCTION(xcache_get_refcount)
+{
+	zval *variable;
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	RETURN_LONG(Z_REFCOUNT_P(variable));
+}
+/* }}} */
+/* {{{ proto bool xcache_get_isref(mixed variable)
+   XCache internal uses only: Check if variable data is marked referenced */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
+	ZEND_ARG_INFO(1, variable)
+ZEND_END_ARG_INFO()
+
+PHP_FUNCTION(xcache_get_isref)
+{
+	zval *variable;
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	RETURN_BOOL(Z_ISREF_P(variable) && Z_REFCOUNT_P(variable) >= 3);
+}
+/* }}} */
 #ifdef HAVE_XCACHE_DPRINT
 /* {{{ proto bool  xcache_dprint(mixed value)
@@ -2428,5 +2456,6 @@
 /* }}} */
 #endif
-/* {{{ proto mixed xcache_get_special_value(zval value) */
+/* {{{ proto mixed xcache_get_special_value(zval value)
+   XCache internal use only: For decompiler to get static value with type fixed */
 PHP_FUNCTION(xcache_get_special_value)
 {
@@ -2453,4 +2482,17 @@
 		RETURN_NULL();
 	}
+}
+/* }}} */
+/* {{{ proto int xcache_get_type(zval value)
+   XCache internal use only for disassembler to get variable type in engine level */
+PHP_FUNCTION(xcache_get_type)
+{
+	zval *value;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
+		return;
+	}
+
+	RETURN_LONG(Z_TYPE_P(value));
 }
 /* }}} */
@@ -2507,4 +2549,5 @@
 #endif
 	PHP_FE(xcache_get_special_value, NULL)
+	PHP_FE(xcache_get_type,          NULL)
 	PHP_FE(xcache_get_op_type,       NULL)
 	PHP_FE(xcache_get_data_type,     NULL)
@@ -2521,4 +2564,6 @@
 	PHP_FE(xcache_unset,             NULL)
 	PHP_FE(xcache_unset_by_prefix,   NULL)
+	PHP_FE(xcache_get_refcount,      NULL)
+	PHP_FE(xcache_get_isref,         arginfo_xcache_get_isref)
 #ifdef HAVE_XCACHE_DPRINT
 	PHP_FE(xcache_dprint,            NULL)
