Fix so templating system does not mess with external (e.g. email) content (#5499)

pull/5507/head
Aleksander Machniak 8 years ago
parent c3e7d93c43
commit d02e6ea45e

@ -57,6 +57,7 @@ CHANGELOG Roundcube Webmail
- Managesieve: Unhide advanced rule controls if there are inputs with errors - Managesieve: Unhide advanced rule controls if there are inputs with errors
- Managesieve: Display warning message when filter form contains errors - Managesieve: Display warning message when filter form contains errors
- Control search engine crawlers via X-Robots-Tag header instead of <meta> and robots.txt (#5098) - Control search engine crawlers via X-Robots-Tag header instead of <meta> and robots.txt (#5098)
- Fix so templating system does not mess with external (e.g. email) content (#5499)
- Fix redundant keep-alive/refresh after session error on compose page (#5500) - Fix redundant keep-alive/refresh after session error on compose page (#5500)
- Fix flickering of header topline in min-mode (#5426) - Fix flickering of header topline in min-mode (#5426)
- Fix bug where folders list would scroll to top when clicking on subscription checkbox (#5447) - Fix bug where folders list would scroll to top when clicking on subscription checkbox (#5447)

@ -31,6 +31,7 @@ class rcmail_output_html extends rcmail_output
protected $message; protected $message;
protected $template_name; protected $template_name;
protected $objects = array();
protected $js_env = array(); protected $js_env = array();
protected $js_labels = array(); protected $js_labels = array();
protected $js_commands = array(); protected $js_commands = array();
@ -638,15 +639,11 @@ EOF;
$output = $hook['content']; $output = $hook['content'];
unset($hook['content']); unset($hook['content']);
// make sure all <form> tags have a valid request token
$output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output);
$this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer);
// remove plugin skin paths from current context // remove plugin skin paths from current context
$this->skin_paths = array_slice($this->skin_paths, count($plugin_skin_paths)); $this->skin_paths = array_slice($this->skin_paths, count($plugin_skin_paths));
if (!$write) { if (!$write) {
return $output; return $this->postrender($output);
} }
$this->write(trim($output)); $this->write(trim($output));
@ -888,6 +885,7 @@ EOF;
{ {
$input = $this->parse_conditions($input); $input = $this->parse_conditions($input);
$input = $this->parse_xml($input); $input = $this->parse_xml($input);
$input = $this->postrender($input);
return $input; return $input;
} }
@ -970,7 +968,7 @@ EOF;
* *
* @return mixed Expression result * @return mixed Expression result
*/ */
protected function eval_expression ($expression) protected function eval_expression($expression)
{ {
$expression = preg_replace( $expression = preg_replace(
array( array(
@ -1012,13 +1010,18 @@ EOF;
* with the appropriate content * with the appropriate content
* *
* @param string $input Input string to parse * @param string $input Input string to parse
* @param bool $reset Reset stored objects
* *
* @return string Altered input string * @return string Altered input string
* @todo Use DOM-parser to traverse template HTML * @todo Use DOM-parser to traverse template HTML
* @todo Maybe a cache. * @todo Maybe a cache.
*/ */
protected function parse_xml($input) protected function parse_xml($input, $reset = true)
{ {
if ($reset) {
$this->objects = array();
}
return preg_replace_callback('/<roundcube:([-_a-z]+)\s+((?:[^>]|\\\\>)+)(?<!\\\\)>/Ui', array($this, 'xml_command'), $input); return preg_replace_callback('/<roundcube:([-_a-z]+)\s+((?:[^>]|\\\\>)+)(?<!\\\\)>/Ui', array($this, 'xml_command'), $input);
} }
@ -1118,7 +1121,7 @@ EOF;
$incl = file_get_contents($path); $incl = file_get_contents($path);
} }
$incl = $this->parse_conditions($incl); $incl = $this->parse_conditions($incl);
$incl = $this->parse_xml($incl); $incl = $this->parse_xml($incl, false);
$incl = $this->fix_paths($incl); $incl = $this->fix_paths($incl);
$this->base_path = $old_base_path; $this->base_path = $old_base_path;
return $incl; return $incl;
@ -1141,14 +1144,15 @@ EOF;
// return code for a specific application object // return code for a specific application object
case 'object': case 'object':
$object = strtolower($attrib['name']); $object = strtolower($attrib['name']);
$content = ''; $content = '';
// we are calling a class/method // we are calling a class/method
if (($handler = $this->object_handlers[$object]) && is_array($handler)) { if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) || if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) ||
(is_string($handler[0]) && class_exists($handler[0]))) (is_string($handler[0]) && class_exists($handler[0])))
$content = call_user_func($handler, $attrib); $content = call_user_func($handler, $attrib);
$external = true;
} }
// execute object handler function // execute object handler function
else if (function_exists($handler)) { else if (function_exists($handler)) {
@ -1211,6 +1215,13 @@ EOF;
// exec plugin hooks for this template object // exec plugin hooks for this template object
$hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => $content)); $hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => $content));
if (strlen($hook['content']) && !empty($external)) {
$object_id = uniqid('TEMPLOBJECT:', true);
$this->objects[$object_id] = $hook['content'];
$hook['content'] = $object_id;
}
return $hook['content']; return $hook['content'];
// return code for a specified eval expression // return code for a specified eval expression
@ -1276,6 +1287,25 @@ EOF;
return $out; return $out;
} }
/**
* Put objects' content back into template output
*/
protected function postrender($output)
{
// insert objects' contents
foreach ($this->objects as $key => $val) {
$output = str_replace($key, $val, $output);
}
// reset objects
$this->objects = array();
// make sure all <form> tags have a valid request token
$output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output);
return $output;
}
/** /**
* Create and register a button * Create and register a button
* *
@ -1540,7 +1570,7 @@ EOF;
* @param string $templ HTML template * @param string $templ HTML template
* @param string $base_path Base for absolute paths * @param string $base_path Base for absolute paths
*/ */
public function _write($templ = '', $base_path = '') protected function _write($templ = '', $base_path = '')
{ {
$output = trim($templ); $output = trim($templ);
@ -1664,6 +1694,8 @@ EOF;
$output = $this->fix_assets_paths($output); $output = $this->fix_assets_paths($output);
} }
$output = $this->postrender($output);
// trigger hook with final HTML content to be sent // trigger hook with final HTML content to be sent
$hook = $this->app->plugins->exec_hook("send_page", array('content' => $output)); $hook = $this->app->plugins->exec_hook("send_page", array('content' => $output));
if (!$hook['abort']) { if (!$hook['abort']) {

Loading…
Cancel
Save