Skip to content

Commit

Permalink
- bugfix a relative sub template path could fail if template_dir path…
Browse files Browse the repository at this point in the history
… did contain /../ #50

  - optimization rework of path normalization
  • Loading branch information
uwetews committed Jun 14, 2015
1 parent e96ee1b commit ecddc1f
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 98 deletions.
4 changes: 4 additions & 0 deletions change_log.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
61 changes: 24 additions & 37 deletions libs/Smarty.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -973,7 +969,7 @@ public function setTemplateDir($template_dir)
{
$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;
Expand Down Expand Up @@ -1021,7 +1017,7 @@ public function setConfigDir($config_dir)
{
$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;
Expand Down Expand Up @@ -1067,10 +1063,7 @@ public function getConfigDir($index = null)
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;
}

Expand All @@ -1083,7 +1076,11 @@ public function setPluginsDir($plugins_dir)
*/
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;
Expand All @@ -1108,7 +1105,7 @@ public function getPluginsDir()
*/
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;
}
Expand All @@ -1135,11 +1132,10 @@ public function getCompileDir()
*/
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;
}

Expand Down Expand Up @@ -1167,23 +1163,21 @@ private function _addDir($dirName, $dir, $key = null)

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;
}
}
}
Expand Down Expand Up @@ -1420,8 +1414,7 @@ public function loadPlugin($plugin_name, $check = true)

// 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);
Expand Down Expand Up @@ -1678,10 +1671,8 @@ public function __destruct()
*/
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])) {
Expand All @@ -1701,10 +1692,8 @@ public function __get($name)
*/
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])) {
Expand Down Expand Up @@ -1750,8 +1739,7 @@ public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $
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;
Expand Down Expand Up @@ -1793,8 +1781,7 @@ public static function muteExpectedErrors()
- 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
Expand Down
115 changes: 54 additions & 61 deletions libs/sysplugins/smarty_internal_resource_file.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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);
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);
$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 ($_template && $_template->parent instanceof Smarty_Internal_Template && (!empty($fileMatch['rel1']) || !empty($fileMatch['rel2']))) {
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);
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;
}
$path = dirname($_template->parent->source->filepath) . DS . $file;
// 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'];
}
$path = $this->normalizePath($path);
// files relative to a template only get one shot
return is_file($file) ? $file : false;
return is_file($path) ? $path : false;
}

$_filepath = null;
if ($source->isConfig) {
$_directories = $source->smarty->getConfigDir();
} else {
$_directories = $source->smarty->getTemplateDir();
}
// template_dir index?
if (!empty($fileMatch['index'])) {
$index = $fileMatch['index'];
Expand All @@ -86,29 +74,24 @@ protected function buildFilepath(Smarty_Template_Source $source, Smarty_Internal
}
}
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
Expand All @@ -117,25 +100,38 @@ protected function buildFilepath(Smarty_Template_Source $source, Smarty_Internal
} 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;
}
$path = str_replace($this->dsMap[DS][0], DS, $path);
while (strrpos($path, $this->dsMap[DS][1]) !== false) {
$path = preg_replace('#([\\\/][.][\\\/])|([\\\/][^\\\/]+[\\\/][.][.][\\\/])#', DS, $path);
}
return is_file($file) ? $file : false;
return $path;
}

/**
Expand Down Expand Up @@ -166,13 +162,10 @@ public function populate(Smarty_Template_Source $source, Smarty_Internal_Templat
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;
Expand Down

0 comments on commit ecddc1f

Please sign in to comment.