diff --git a/NEWS b/NEWS
index ba34b568..a1b44e2e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+ - added trusted_dir functionality (Monte)
+ - consolidated security tests to one function (Monte)
+ - prepend SMARTY_DIR to default directories in class constructor (Monte,
+ Ricard Pillosu)
+ - append _smarty_ to variable names in fetch() class function to avoid
+ namespace conflicts (Monte)
+ - fixed bug in _rm_auto with catendated null values (Monte, Thomas Pundt)
- introduced $compile_id class variable that can be used to set persistent
compile identifier across multiple display calls. (Andrei)
- fixed bug with concatenated null cache and compile identifiers. (Andrei)
diff --git a/QUICKSTART b/QUICKSTART
index 511bd4d0..59cd6486 100644
--- a/QUICKSTART
+++ b/QUICKSTART
@@ -1,9 +1,9 @@
SMARTY QUICKSTART GUIDE
-----------------------
-So you don't like reading documentation either? This guide is to help you
-immediately get your feet wet in Smarty, get a feel for how it works and if it
-will work for you.
+Welcome to the Smarty QUICKSTART guide. This guide is to help you immediately
+get your feet wet in Smarty, get a feel for how it works and if it will work
+for you.
We make a few assumptions here, such as you already have PHP installed on your
web server, you know the basics of unix file permissions, the basics of PHP and
@@ -15,9 +15,11 @@ INSTALLATION
Unpack the Smarty tarball. You will see four class files: Smarty.class.php,
Smarty.addons.php, Smarty_Compiler.class.php and Config_File.class.php. You
-will need to have all three of these files somewhere in your PHP include path,
+will need to have all four of these files somewhere in your PHP include path,
so when you call require("Smarty.class.php") from within your application, it
-can find the class.
+can find the class. Alternatively, you can set the SMARTY_DIR constant in your
+application, and Smarty will use that directory as the path to the Smarty
+class files. Be sure the path ends with a slash!
Smarty uses the PEAR libraries for some of its error handling routines. PEAR
libraries come with the distribution of PHP. Be sure that the path to these
@@ -68,8 +70,8 @@ to PEAR.php (named like this in some distributions?)
Now we need to create two files, index.php and templates/index.tpl. index.php
is the file that we will be calling from our web browser. index.tpl is the file
-that Smarty will use as its template file. (.tpl files are never called
-directly from the browser, only Smarty calls them.)
+that Smarty will use as its template file. (Smarty template files are never
+accessed directly from the browser, only the Smarty class accesses them.)
--------- index.php --------
@@ -96,7 +98,8 @@ http://your.host.com/Smarty/index.php
You should see "Hello, Ned!" in your browser. If not, retrace the steps above
and make sure you follow the instructions exactly as they say. Also check the
installation instructions in the documenation, and check your installation of
-PHP to be sure things are setup properly.
+PHP to be sure things are setup properly. If your still having problems, ask a
+question on the mailing list, which you can find in the FAQ.
You see "Hello, Ned!" in your browser? Good!
@@ -108,16 +111,13 @@ templates_c directory. Out of sight, out of mind. You don't need to worry about
it, Smarty takes care of this technical stuff. Out of curiosity, you can see
how your templates look as compiled php scripts, but please don't edit them!
-Now that we have Smarty functioning properly, let's get into the guts of Smarty
-and learn what thing is all about. I'll try to briefly show examples to cover
-the most important features of Smarty.
-
+Now that we have Smarty functioning properly, let's take a look at a few
+features of the template language.
ASSIGNING VARIABLES
-------------------
-Assigning variables to the template is very similar to the ways other template
-engines do it. Example:
+Assigning variables to the template is fairly straight forward. Example:
--------- index.php --------
@@ -195,10 +195,11 @@ $smarty->display("index.tpl");
Notice we are passing the variable "title" when we include the header.tpl file.
You can pass as many variables as you want. The included file inherits all the
current template vars, plus any that are passed to it. The passed variables are
-only available within the scope of the included file. Also notice the way the
-$title variable is printed to the template. It uses a variable modifier called
-"default". Printing {$title|default:"no title"} means that if the value of
-$title is empty, the text "no title" will be printed instead of nothing.
+only available within the scope of the included file (and any files it may
+include.) Also notice the way the $title variable is printed to the template.
+It uses a variable modifier called "default". Printing {$title|default:"no
+title"} means that if the value of $title is empty, the text "no title" will be
+printed instead of nothing.
IF/ELSEIF/ELSE
--------------
@@ -242,22 +243,23 @@ $smarty->display("index.tpl");
--------- templates/index.tpl --------
{include file="header.tpl" title="Home Page"}
{section name=people loop=$FirstName}
- {%people.rownum%} {$FirstName[people]} {$LastName[people]}
+ {$smarty.section.people.rownum} {$FirstName[people]} {$LastName[people]}
{sectionelse}
There are no values to loop through.
{/section}
- There were {%people.loop%} names in this list. + There were {$smarty.section.people.loop} names in this list. {include file="footer.tpl"} - -Notice that when printing variables inside of the section, the section name must -be referenced in the name of the variable being displayed. This lets Smarty -understand that you want to print the value in the array postion indexed by the -current loop value. There are also internal template variables available within -the section that display the loop iteration and the total number of times the -section is looped. Also note the {sectionelse}. This would have been displayed -had the loop array $FirstName been empty. +Here we are introducing the {$smarty} variable, which is used to reference +values internal to the template. Notice that when printing variables inside of +the section, the section name must be referenced in the name of the variable +being displayed. This lets Smarty understand that you want to print the value +in the array postion indexed by the current loop value. There are also internal +template variables available within the section that display the loop iteration +and the total number of times the section is looped. Also note the +{sectionelse}. This would have been displayed if looped array $FirstName was +empty. You can access keys of arrays with this syntax: @@ -282,7 +284,7 @@ $smarty->display("index.tpl"); There are no values to loop through. {/section}
- There were {%people.loop%} names in this list.
+ There were {$smarty.section.people.loop} names in this list.
{include file="footer.tpl"}
@@ -311,22 +313,67 @@ $smarty->display("index.tpl");
--------- templates/index.tpl --------
{include file="header.tpl" title="Home Page"}
{section name=people loop=$FirstName}
- {%people.rownum%} {$FirstName[people]} {$LastName[people]}
+ {$smarty.section.people.rownum} {$FirstName[people]} {$LastName[people]}
{section name=contacts loop=$ContactNames[people]}
{* for fun, lets bold every other row *}
- {if %contacts.rownum% is even}{/if}
+ {if $smarty.section.contacts.rownum is even}{/if}
{$ContactNames[people][contacts]}: {$ContactVals[people][contacts]}
- {if %contacts.rownum% is even}{/if}
+ {if $smarty.section.contacts.rownum is even}{/if}
{/section}
{sectionelse}
There are no values to loop through.
{/section}
- There were {%people.loop%} names in this list.
+ There were {$smarty.sectin.people.loop} names in this list.
{include file="footer.tpl"}
+FOREACH
+-------
+
+There is also an alternative way to loop through associative arrays that may be
+a bit easier for you, especially if you only need to loop over one variable.
+
+Here's an example that works with the default index.php that comes with Smarty:
+
+{foreach name=outer item=contact from=$contacts}
+ {foreach key=key item=item from=$contact}
+ {$smarty.foreach.outer.iteration}. contact {$key}: {$item}
+ {/foreach}
+{foreachelse}
+ no contacts
+{/foreach}
+
+Possible attributes:
+
+from: the array you are looping through
+item: the name of the variable that is the current element
+key: the name of the variable that is the current key (optional)
+name: the name of the of foreach (optional)
+
+The 'name' has to be used if you want to refer to any of the foreach
+properties.
+
+Properties (if 'name' is used):
+
+name: the name of foreach
+iteration: current iteration
+total: total number of iterations
+first: if it's first iteration
+last: it it's last iteration
+show: if foreach was shown at all
+
+Here's another neat example, dumps all properties of foreach:
+
+{foreach name=outer item=contact from=$contacts}
+ {foreach key=key item=item from=$smarty.foreach.outer}
+ {$key}: {$item}
+ {/foreach}
+{/foreach}
+
+
This should be enough to get your feet wet. Also, check out config file
variables, built-in functions, custom functions, variable modifiers, all sorts
-of good stuff. Now go read the documentation, and Good Luck!
+of good stuff. Now go read the documentation, join the mailing list and have
+fun!
diff --git a/Smarty.class.php b/Smarty.class.php
index 81d4f807..8dbbc0fd 100644
--- a/Smarty.class.php
+++ b/Smarty.class.php
@@ -127,6 +127,9 @@ class Smarty
'PHP_TAGS' => false,
'MODIFIER_FUNCS' => array('count')
);
+ var $trusted_dir = array(); // array of directories where trusted templates
+ // reside ($security is disabled during their
+ // execution.)
var $left_delimiter = '{'; // template tag delimiters.
var $right_delimiter = '}';
@@ -212,6 +215,18 @@ class Smarty
\*======================================================================*/
function Smarty()
{
+ $this->template_dir = SMARTY_DIR.$this->template_dir;
+ $this->config_dir = SMARTY_DIR.$this->config_dir;
+ $this->compile_dir = SMARTY_DIR.$this->compile_dir;
+ $this->cache_dir = SMARTY_DIR.$this->cache_dir;
+
+ for($x=0; $x < count($this->secure_dir); $x++) {
+ $this->secure_dir[$x] = SMARTY_DIR.$this->secure_dir[$x];
+ }
+ for($x=0; $x < count($this->trusted_dir); $x++) {
+ $this->trusted_dir[$x] = SMARTY_DIR.$this->trusted_dir[$x];
+ }
+
foreach ($this->global_assign as $key => $var_name) {
if (is_array($var_name)) {
foreach ($var_name as $var) {
@@ -511,7 +526,7 @@ class Smarty
Function: fetch()
Purpose: executes & returns or displays the template results
\*======================================================================*/
- function fetch($smarty_tpl_file, $smarty_cache_id = null, $smarty_compile_id = null, $smarty_display = false)
+ function fetch($_smarty_tpl_file, $_smarty_cache_id = null, $_smarty_compile_id = null, $_smarty_display = false)
{
global $HTTP_SERVER_VARS, $QUERY_STRING, $HTTP_COOKIE_VARS;
@@ -524,27 +539,24 @@ class Smarty
// capture time for debugging info
$debug_start_time = $this->_get_microtime();
$this->_smarty_debug_info[] = array('type' => 'template',
- 'filename' => $smarty_tpl_file,
+ 'filename' => $_smarty_tpl_file,
'depth' => 0);
$included_tpls_idx = count($this->_smarty_debug_info) - 1;
}
-
- if (!isset($compile_id))
- $compile_id = $this->compile_id;
-
+ $this->_compile_id = $_smarty_compile_id;
$this->_inclusion_depth = 0;
if ($this->caching) {
- $this->_cache_info[] = array('template', $smarty_tpl_file);
+ $this->_cache_info[] = array('template', $_smarty_tpl_file);
- if ($this->_read_cache_file($smarty_tpl_file, $smarty_cache_id, $smarty_compile_id, $smarty_results)) {
+ if ($this->_read_cache_file($_smarty_tpl_file, $_smarty_cache_id, $_smarty_compile_id, $_smarty_results)) {
if ($this->insert_tag_check) {
- $smarty_results = $this->_process_cached_inserts($smarty_results);
+ $_smarty_results = $this->_process_cached_inserts($_smarty_results);
}
- if ($smarty_display) {
- echo $smarty_results;
+ if ($_smarty_display) {
+ echo $_smarty_results;
if ($this->debugging)
{
// capture time for debugging info
@@ -554,7 +566,7 @@ class Smarty
}
return;
} else {
- return $smarty_results;
+ return $_smarty_results;
}
}
}
@@ -577,51 +589,66 @@ class Smarty
'files' => array()));
if ($this->show_info_header) {
- $info_header = ''."\n\n";
+ $_smarty_info_header = ''."\n\n";
} else {
- $info_header = '';
+ $_smarty_info_header = '';
}
- $compile_path = $this->_get_compile_path($smarty_tpl_file);
+ $compile_path = $this->_get_compile_path($_smarty_tpl_file);
+ if($this->security && $this->_is_trusted($_smarty_tpl_file)) {
+ $_smarty_trusted = true;
+ $this->security = false;
+ } else {
+ $_smarty_trusted = false;
+ }
// if we just need to display the results, don't perform output
// buffering - for speed
- if ($smarty_display && !$this->caching) {
- echo $info_header;
- if ($this->_process_template($smarty_tpl_file, $compile_path))
+ if ($_smarty_display && !$this->caching) {
+ echo $_smarty_info_header;
+ if ($this->_process_template($_smarty_tpl_file, $compile_path))
{
if ($this->show_info_include) {
- echo "\n\n";
+ echo "\n\n";
}
- include($compile_path);
+ if($this->security && $this->_is_trusted($_smarty_tpl_file)) {
+ $this->security = false;
+ include($compile_path);
+ $this->security = true;
+ } else {
+ include($compile_path);
+ }
if ($this->show_info_include) {
- echo "\n\n";
+ echo "\n\n";
}
}
} else {
ob_start();
- echo $info_header;
- if ($this->_process_template($smarty_tpl_file, $compile_path))
+ echo $_smarty_info_header;
+ if ($this->_process_template($_smarty_tpl_file, $compile_path))
{
if ($this->show_info_include) {
- echo "\n\n";
+ echo "\n\n";
}
- include($compile_path);
+ include($compile_path);
if ($this->show_info_include) {
- echo "\n\n";
+ echo "\n\n";
}
}
- $smarty_results = ob_get_contents();
+ $_smarty_results = ob_get_contents();
ob_end_clean();
}
+ if($_smarty_trusted) {
+ $this->security = true;
+ }
if ($this->caching) {
- $this->_write_cache_file($smarty_tpl_file, $smarty_cache_id, $smarty_compile_id, $smarty_results);
- $smarty_results = $this->_process_cached_inserts($smarty_results);
+ $this->_write_cache_file($_smarty_tpl_file, $_smarty_cache_id, $_smarty_compile_id, $_smarty_results);
+ $_smarty_results = $this->_process_cached_inserts($_smarty_results);
}
- if ($smarty_display) {
- if (isset($smarty_results)) { echo $smarty_results; }
+ if ($_smarty_display) {
+ if (isset($_smarty_results)) { echo $_smarty_results; }
if ($this->debugging)
{
// capture time for debugging info
@@ -631,7 +658,7 @@ class Smarty
}
return;
} else {
- if (isset($smarty_results)) { return $smarty_results; }
+ if (isset($_smarty_results)) { return $_smarty_results; }
}
}
@@ -699,13 +726,108 @@ function _generate_debug_output() {
return $results;
}
+/*======================================================================*\
+ Function: _is_trusted()
+ Purpose: determins if a template is trusted or not. If trusted,
+ $security is disabled during its execution.
+\*======================================================================*/
+function _is_trusted($tpl_file) {
+
+ static $_trusted_tpls = array();
+
+ if(in_array($tpl_file,$_trusted_tpls)) {
+ return true;
+ }
+
+ $_smarty_trusted = false;
+ if($this->security && !empty($this->trusted_dir)) {
+ // see if template file is within a trusted directory. If so,
+ // disable security during the execution of the template.
+
+ // if template is on local file system, check if trusted
+ $tpl_path_parts = explode(':', $tpl_file, 2);
+ if (count($tpl_path_parts) == 1) {
+ // no resource type, treat as type "file"
+ $resource_type = 'file';
+ $resource_name = $tpl_path_parts[0];
+ } else {
+ $resource_type = $tpl_path_parts[0];
+ $resource_name = $tpl_path_parts[1];
+ }
+ if ($resource_type == 'file') {
+ if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/",$resource_name)) {
+ // relative pathname to $template_dir
+ $resource_name = $this->template_dir.'/'.$resource_name;
+ }
+ foreach ($this->trusted_dir as $curr_dir) {
+ if (substr(realpath($resource_name),0,strlen(realpath($curr_dir))) == realpath($curr_dir)) {
+ $_smarty_trusted = true;
+ $_trusted_tpls[] = $tpl_file;
+ break;
+ }
+ }
+ } else {
+ // resource is not on local file system
+ $_smarty_trusted = false;
+ }
+ }
+ return $_smarty_trusted;
+}
+
+/*======================================================================*\
+ Function: _is_secure()
+ Purpose: determins if a template is secure or not.
+\*======================================================================*/
+function _is_secure($tpl_file) {
+
+ static $_secure_tpls = array();
+
+ if(!$this->security || $this->security_settings['INCLUDE_ANY'] || in_array($tpl_file,$_secure_tpls)) {
+ return true;
+ }
+
+ $_smarty_secure = false;
+ // if template is on local file system, check if secure
+ $tpl_path_parts = explode(':', $tpl_file, 2);
+ if (count($tpl_path_parts) == 1) {
+ // no resource type, treat as type "file"
+ $resource_type = 'file';
+ $resource_name = $tpl_path_parts[0];
+ } else {
+ $resource_type = $tpl_path_parts[0];
+ $resource_name = $tpl_path_parts[1];
+ }
+
+ if ($resource_type == 'file') {
+ if(!empty($this->secure_dir)) {
+ if (!preg_match("/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/",$resource_name)) {
+ // relative pathname to $template_dir
+ $resource_name = $this->template_dir.'/'.$resource_name;
+ }
+ foreach ($this->secure_dir as $curr_dir) {
+ if (substr(realpath($resource_name),0,strlen(realpath($curr_dir))) == realpath($curr_dir)) {
+ $_smarty_secure = true;
+ $_secure_tpls[] = $tpl_file;
+ break;
+ }
+ }
+ }
+ } else {
+ // resource is not on local file system
+ $_smarty_secure = true;
+ }
+
+ return $_smarty_secure;
+}
+
+
/*======================================================================*\
Function: _process_template()
Purpose:
\*======================================================================*/
function _process_template($tpl_file, $compile_path)
- {
- // test if template needs to be compiled
+ {
+ // test if template needs to be compiled
if (!$this->force_compile && $this->_compiled_template_exists($compile_path)) {
if (!$this->compile_check) {
// no need to check if the template needs recompiled
@@ -787,6 +909,11 @@ function _generate_debug_output() {
\*======================================================================*/
function _fetch_template_info($tpl_path, &$template_source, &$template_timestamp, $get_source=true)
{
+ if ($this->security && !$this->_is_secure($tpl_path) && !$this->_is_trusted($tpl_path)) {
+ $this->_trigger_error_msg("(secure mode) accessing \"$tpl_path\" is not allowed");
+ return false;
+ }
+
// split tpl_path by the first colon
$tpl_path_parts = explode(':', $tpl_path, 2);
@@ -814,20 +941,6 @@ function _generate_debug_output() {
$this->_trigger_error_msg("unable to read template resource: \"$tpl_path\"");
return false;
}
- // if security is on, make sure template comes from a $secure_dir
- if ($this->security && !$this->security_settings['INCLUDE_ANY']) {
- $resource_is_secure = false;
- foreach ($this->secure_dir as $curr_dir) {
- if (substr(realpath($resource_name),0,strlen(realpath($curr_dir))) == realpath($curr_dir)) {
- $resource_is_secure = true;
- break;
- }
- }
- if (!$resource_is_secure) {
- $this->_trigger_error_msg("(secure mode) including \"$resource_name\" is not allowed");
- return false;
- }
- }
break;
default:
if (isset($this->resource_funcs[$resource_type])) {
@@ -905,17 +1018,27 @@ function _generate_debug_output() {
array_unshift($this->_config, $this->_config[0]);
$compile_path = $this->_get_compile_path($_smarty_include_tpl_file);
+
+ if($this->security && $this->_is_trusted($_smarty_include_tpl_file)) {
+ $_smarty_trusted = true;
+ $this->security = false;
+ } else {
+ $_smarty_trusted = false;
+ }
+
if ($this->_process_template($_smarty_include_tpl_file, $compile_path)) {
if ($this->show_info_include) {
echo "\n\n";
}
-
include($compile_path);
-
if ($this->show_info_include) {
echo "\n\n";
}
}
+
+ if ($_smarty_trusted) {
+ $this->security = true;
+ }
array_shift($this->_config);
$this->_inclusion_depth--;
diff --git a/docs.sgml b/docs.sgml
index eaecbbbc..ceb9bc62 100644
--- a/docs.sgml
+++ b/docs.sgml
@@ -1697,11 +1697,11 @@ email: zaphod@slartibartfast.com<br>