- bugfix a relative sub template path could fail if template_dir path did contain /../ https://github.com/smarty-php/smarty/issues/50

- optimization rework of path normalization
This commit is contained in:
Uwe Tews
2015-06-14 03:30:31 +02:00
parent e96ee1b5d6
commit ecddc1f720
3 changed files with 89 additions and 105 deletions

View File

@@ -1,4 +1,8 @@
 ===== 3.1.25-dev===== (xx.xx.2015)
14.06.2015
- bugfix a relative sub template path could fail if template_dir path did contain /../ https://github.com/smarty-php/smarty/issues/50
- optimization rework of path normalization
13.06.2015
- bugfix a custom cache resource using smarty_cachereource_keyvaluestore.php did fail if php.ini mbstring.func_overload = 2 (forum topic 25568)

View File

@@ -111,7 +111,7 @@ class Smarty extends Smarty_Internal_TemplateBase
/**
* smarty version
*/
const SMARTY_VERSION = '3.1.25-dev/6';
const SMARTY_VERSION = '3.1.25-dev/7';
/**
* define variable scopes
@@ -206,8 +206,7 @@ class Smarty extends Smarty_Internal_TemplateBase
/**
* contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors()
*/
public static $_muted_directories = array('./templates_c/' => null,
'./cache/' => null);
public static $_muted_directories = array('./templates_c/' => null, './cache/' => null);
/**
* Flag denoting if Multibyte String functions are available
@@ -617,10 +616,7 @@ class Smarty extends Smarty_Internal_TemplateBase
*
* @var array
*/
public $plugin_search_order = array('function',
'block',
'compiler',
'class');
public $plugin_search_order = array('function', 'block', 'compiler', 'class');
/**
* registered objects
@@ -973,7 +969,7 @@ class Smarty extends Smarty_Internal_TemplateBase
{
$this->template_dir = array();
foreach ((array) $template_dir as $k => $v) {
$this->template_dir[$k] = rtrim(strtr($v, '\\', '/'), '/') . '/';
$this->template_dir[$k] = rtrim($v, '/\\') . DS;
}
$this->joined_template_dir = join(' # ', $this->template_dir);
return $this;
@@ -1021,7 +1017,7 @@ class Smarty extends Smarty_Internal_TemplateBase
{
$this->config_dir = array();
foreach ((array) $config_dir as $k => $v) {
$this->config_dir[$k] = rtrim(strtr($v, '\\', '/'), '/') . '/';
$this->config_dir[$k] = rtrim($v, '/\\') . DS;
}
$this->joined_config_dir = join(' # ', $this->config_dir);
return $this;
@@ -1067,10 +1063,7 @@ class Smarty extends Smarty_Internal_TemplateBase
public function setPluginsDir($plugins_dir)
{
$this->plugins_dir = array();
foreach ((array) $plugins_dir as $k => $v) {
$this->plugins_dir[$k] = rtrim(strtr($v, '\\', '/'), '/') . '/';
}
$this->_is_file_cache = array();
$this->addPluginsDir($plugins_dir);
return $this;
}
@@ -1083,7 +1076,11 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
public function addPluginsDir($plugins_dir)
{
$this->_addDir('plugins_dir', $plugins_dir);
// make sure we're dealing with an array
$this->plugins_dir = (array) $this->plugins_dir;
foreach ((array) $plugins_dir as $v) {
$this->plugins_dir[] = rtrim($v, '/\\') . DS;
}
$this->plugins_dir = array_unique($this->plugins_dir);
$this->_is_file_cache = array();
return $this;
@@ -1108,7 +1105,7 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
public function setCompileDir($compile_dir)
{
$this->compile_dir = rtrim(strtr($compile_dir, '\\', '/'), '/') . '/';
$this->compile_dir = rtrim($compile_dir, '/\\') . DS;
if (!isset(Smarty::$_muted_directories[$this->compile_dir])) {
Smarty::$_muted_directories[$this->compile_dir] = null;
}
@@ -1135,11 +1132,10 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
public function setCacheDir($cache_dir)
{
$this->cache_dir = rtrim(strtr($cache_dir, '\\', '/'), '/') . '/';
$this->cache_dir = rtrim($cache_dir, '/\\') . DS;
if (!isset(Smarty::$_muted_directories[$this->cache_dir])) {
Smarty::$_muted_directories[$this->cache_dir] = null;
}
return $this;
}
@@ -1167,23 +1163,21 @@ class Smarty extends Smarty_Internal_TemplateBase
if (is_array($dir)) {
foreach ($dir as $k => $v) {
$v = rtrim(strtr($v, '\\', '/'), '/') . '/';
if (is_int($k)) {
// indexes are not merged but appended
$this->{$dirName}[] = $v;
$this->{$dirName}[] = rtrim($v, '/\\') . DS;
} else {
// string indexes are overridden
$this->{$dirName}[$k] = $v;
$this->{$dirName}[$k] = rtrim($v, '/\\') . DS;
}
}
} else {
$v = rtrim(strtr($dir, '\\', '/'), '/') . '/';
if ($key !== null) {
// override directory at specified index
$this->{$dirName}[$key] = $v;
$this->{$dirName}[$key] = rtrim($dir, '/\\') . DS;
} else {
// append new directory
$this->{$dirName}[] = $v;
$this->{$dirName}[] = rtrim($dir, '/\\') . DS;
}
}
}
@@ -1420,8 +1414,7 @@ class Smarty extends Smarty_Internal_TemplateBase
// loop through plugin dirs and find the plugin
foreach ($this->getPluginsDir() as $_plugin_dir) {
$names = array($_plugin_dir . $_plugin_filename,
$_plugin_dir . strtolower($_plugin_filename),);
$names = array($_plugin_dir . $_plugin_filename, $_plugin_dir . strtolower($_plugin_filename),);
foreach ($names as $file) {
if (isset($this->_is_file_cache[$file]) ? $this->_is_file_cache[$file] : $this->_is_file_cache[$file] = is_file($file)) {
require_once($file);
@@ -1678,10 +1671,8 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
public function __get($name)
{
$allowed = array('template_dir' => 'getTemplateDir',
'config_dir' => 'getConfigDir',
'plugins_dir' => 'getPluginsDir',
'compile_dir' => 'getCompileDir',
$allowed = array('template_dir' => 'getTemplateDir', 'config_dir' => 'getConfigDir',
'plugins_dir' => 'getPluginsDir', 'compile_dir' => 'getCompileDir',
'cache_dir' => 'getCacheDir',);
if (isset($allowed[$name])) {
@@ -1701,10 +1692,8 @@ class Smarty extends Smarty_Internal_TemplateBase
*/
public function __set($name, $value)
{
$allowed = array('template_dir' => 'setTemplateDir',
'config_dir' => 'setConfigDir',
'plugins_dir' => 'setPluginsDir',
'compile_dir' => 'setCompileDir',
$allowed = array('template_dir' => 'setTemplateDir', 'config_dir' => 'setConfigDir',
'plugins_dir' => 'setPluginsDir', 'compile_dir' => 'setCompileDir',
'cache_dir' => 'setCacheDir',);
if (isset($allowed[$name])) {
@@ -1750,8 +1739,7 @@ class Smarty extends Smarty_Internal_TemplateBase
unset(Smarty::$_muted_directories[$key]);
continue;
}
$dir = array('file' => $file,
'length' => strlen($file),);
$dir = array('file' => $file, 'length' => strlen($file),);
}
if (!strncmp($errfile, $dir['file'], $dir['length'])) {
$_is_muted_directory = true;
@@ -1793,8 +1781,7 @@ class Smarty extends Smarty_Internal_TemplateBase
- between file_exists() and filemtime() a possible race condition is opened,
which does not exist using the simple @filemtime() approach.
*/
$error_handler = array('Smarty',
'mutingErrorHandler');
$error_handler = array('Smarty', 'mutingErrorHandler');
$previous = set_error_handler($error_handler);
// avoid dead loops

View File

@@ -18,6 +18,8 @@
class Smarty_Internal_Resource_File extends Smarty_Resource
{
private $dsMap = array('/' => array(array('\\', '/./'), '/.'), '\\' => array(array('/', '\\.\\'), '\\.'));
/**
* build template filepath by traversing the template_dir array
*
@@ -29,44 +31,30 @@ class Smarty_Internal_Resource_File extends Smarty_Resource
*/
protected function buildFilepath(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null)
{
$file = str_replace(array('\\', '/./'), '/', $source->name);
$file = $source->name;
preg_match('#^(?P<absolute>[\\\/]|[a-zA-Z]:[\\\/])|(\[(?P<index>[^\]]+)\])|(?P<rel>\.[\\\/])#', $file, $fileMatch);
// save basename
if (!empty($fileMatch['absolute'])) {
$file = $this->normalizePath($file);
return is_file($file) ? $file : false;
}
// go relative to a given template?
if (!empty($fileMatch['rel']) && $_template && $_template->parent instanceof Smarty_Internal_Template) {
if ($_template->parent->source->type != 'file' && $_template->parent->source->type != 'extends' && !$_template->parent->allow_relative_path) {
throw new SmartyException("Template '{$file}' cannot be relative to template of resource type '{$_template->parent->source->type}'");
}
$path = dirname($_template->parent->source->filepath) . DS . $file;
// normalize path
$path = $this->normalizePath($path);
// files relative to a template only get one shot
return is_file($path) ? $path : false;
}
if ($source->isConfig) {
$_directories = $source->smarty->getConfigDir();
} else {
$_directories = $source->smarty->getTemplateDir();
}
preg_match('#^((?P<absolute>[\/]|[a-zA-Z]:[\/])|(\[(?P<index>[^\]]+)\])|((?P<rel1>\.[\/])?(?P<rel2>(\.\.[\/])*))|(?P<skip>[\/]))?(?P<file>.+)$#', $file, $fileMatch);
// save basename
if (!empty($fileMatch['absolute'])) {
return is_file($file) ? $file : false;
}
// go relative to a given template?
if ($_template && $_template->parent instanceof Smarty_Internal_Template && (!empty($fileMatch['rel1']) || !empty($fileMatch['rel2']))) {
if ($_template->parent->source->type != 'file' && $_template->parent->source->type != 'extends' && !$_template->parent->allow_relative_path) {
throw new SmartyException("Template '{$file}' cannot be relative to template of resource type '{$_template->parent->source->type}'");
}
$path = dirname($_template->parent->source->filepath);
if (!preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $path)) {
// the path gained from the parent template is relative to the current working directory
// as expansions (like include_path) have already been done
$path = str_replace('\\', '/', getcwd()) . '/' . $path;
}
// normalize path
$path = str_replace(array('\\', './'), array('/', ''), $path);
// simple relative
if (!empty($fileMatch['rel1'])) {
$file = $path . '/' . $fileMatch['file'];
} else {
for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../'); $i ++) {
$path = substr($path, 0, strrpos($path, '/'));
}
$file = $path . '/' . $fileMatch['file'];
}
// files relative to a template only get one shot
return is_file($file) ? $file : false;
}
$_filepath = null;
// template_dir index?
if (!empty($fileMatch['index'])) {
$index = $fileMatch['index'];
@@ -86,29 +74,24 @@ class Smarty_Internal_Resource_File extends Smarty_Resource
}
}
if ($_directory) {
$_filepath = $_directory . $fileMatch['file'];
if (is_file($_filepath)) {
return $_filepath;
preg_match('#\](.+)$#', $file, $fileMatch);
$path = $_directory . $fileMatch[1];
$path = $this->normalizePath($path);
if (is_file($path)) {
return $path;
}
} else {
// index not found
return false;
}
}
// relative file name?
foreach ($_directories as $_directory) {
if (empty($fileMatch['rel2'])) {
$_filepath = $_directory . $fileMatch['file'];
} else {
if (false === strpos($_directory, '..')) {
for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../') + 1; $i ++) {
$_directory = substr($_directory, 0, strrpos($_directory, '/'));
}
$_filepath = $_directory . '/' . $fileMatch['file'];
} else {
$_filepath = $_directory . $file;
}
}
if (is_file($_filepath)) {
return $_filepath;
$_filepath = $_directory . $file;
$path = $this->normalizePath($_filepath);
if (is_file($path)) {
return $path;
}
if ($source->smarty->use_include_path && !preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $_directory)) {
// try PHP include_path
@@ -117,25 +100,38 @@ class Smarty_Internal_Resource_File extends Smarty_Resource
} else {
$_filepath = Smarty_Internal_Get_Include_Path::getIncludePath($_filepath);
}
if ($_filepath !== false) {
if (is_file($_filepath)) {
return $_filepath;
$path = $this->normalizePath($_filepath);
if (is_file($path)) {
return $path;
}
}
}
}
// Could be relative to cwd
$path = str_replace('\\', '/', getcwd());
if (empty($fileMatch['rel2'])) {
$file = $path . '/' . $fileMatch['file'];
} else {
for ($i = 1; $i <= substr_count($fileMatch['rel2'], '../'); $i ++) {
$path = substr($path, 0, strrpos($path, '/'));
}
$file = $path . '/' . $fileMatch['file'];
$path = $this->normalizePath(getcwd() . DS . $file);
return is_file($path) ? $path : false;
}
/**
* Normalize path
* - remove /./ and /../
* - make it absolute
*
* @param string $path file path
*
* @return string
*/
public function normalizePath($path)
{
if ($path[0] == '.') {
$path = getcwd() . DS . $path;
}
return is_file($file) ? $file : false;
$path = str_replace($this->dsMap[DS][0], DS, $path);
while (strrpos($path, $this->dsMap[DS][1]) !== false) {
$path = preg_replace('#([\\\/][.][\\\/])|([\\\/][^\\\/]+[\\\/][.][.][\\\/])#', DS, $path);
}
return $path;
}
/**
@@ -166,13 +162,10 @@ class Smarty_Internal_Resource_File extends Smarty_Resource
if (is_object($source->smarty->security_policy)) {
$source->smarty->security_policy->isTrustedResourceDir($source->filepath);
}
$source->uid = sha1(getcwd() . $source->filepath);
$source->exists = true;
$source->uid = sha1($source->filepath);
if ($source->smarty->compile_check && !isset($source->timestamp)) {
$source->timestamp = $source->exists = is_file($source->filepath);
if ($source->exists) {
$source->timestamp = @filemtime($source->filepath);
}
$source->timestamp = @filemtime($source->filepath);
}
} else {
$source->timestamp = false;