From 0344b168276f80189e2254c75a762aff5b517b6b Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Sun, 22 May 2016 06:32:57 -0400
Subject: [PATCH] Fix priority icon(s) position
---
program/include/rcmail_output_html.php | 172 ++++++++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 145 insertions(+), 27 deletions(-)
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
index 026e9f8..8dda8c3 100644
--- a/program/include/rcmail_output_html.php
+++ b/program/include/rcmail_output_html.php
@@ -1,6 +1,6 @@
<?php
-/*
+/**
+-----------------------------------------------------------------------+
| program/include/rcmail_output_html.php |
| |
@@ -18,7 +18,6 @@
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
-
/**
* Class to create HTML page output using a skin template
@@ -45,6 +44,8 @@
protected $footer = '';
protected $body = '';
protected $base_path = '';
+ protected $assets_path;
+ protected $assets_dir = RCUBE_INSTALL_PATH;
protected $devel_mode = false;
// deprecated names of templates used before 0.5
@@ -79,6 +80,8 @@
$skin = $this->config->get('skin');
$this->set_skin($skin);
$this->set_env('skin', $skin);
+
+ $this->set_assets_path($this->config->get('assets_path'), $this->config->get('assets_dir'));
if (!empty($_REQUEST['_extwin']))
$this->set_env('extwin', 1);
@@ -145,6 +148,55 @@
}
/**
+ * Parse and set assets path
+ *
+ * @param string Assets path (relative or absolute URL)
+ */
+ public function set_assets_path($path, $fs_dir = null)
+ {
+ if (empty($path)) {
+ return;
+ }
+
+ $path = rtrim($path, '/') . '/';
+
+ // handle relative assets path
+ if (!preg_match('|^https?://|', $path) && $path[0] != '/') {
+ // save the path to search for asset files later
+ $this->assets_dir = $path;
+
+ $base = preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI']);
+ $base = rtrim($base, '/');
+
+ // remove url token if exists
+ if ($len = intval($this->config->get('use_secure_urls'))) {
+ $_base = explode('/', $base);
+ $last = count($_base) - 1;
+ $length = $len > 1 ? $len : 16; // as in rcube::get_secure_url_token()
+
+ // we can't use real token here because it
+ // does not exists in unauthenticated state,
+ // hope this will not produce false-positive matches
+ if ($last > -1 && preg_match('/^[a-f0-9]{' . $length . '}$/', $_base[$last])) {
+ $path = '../' . $path;
+ }
+ }
+ }
+
+ // set filesystem path for assets
+ if ($fs_dir) {
+ if ($fs_dir[0] != '/') {
+ $fs_dir = realpath(RCUBE_INSTALL_PATH . $fs_dir);
+ }
+ // ensure the path ends with a slash
+ $this->assets_dir = rtrim($fs_dir, '/') . '/';
+ }
+
+ $this->assets_path = $path;
+ $this->set_env('assets_path', $path);
+ }
+
+ /**
* Getter for the current page title
*
* @return string The page title
@@ -172,6 +224,17 @@
*/
public function set_skin($skin)
{
+ // Sanity check to prevent from path traversal vulnerability (#1490620)
+ if (strpos($skin, '/') !== false || strpos($skin, "\\") !== false) {
+ rcube::raise_error(array(
+ 'file' => __FILE__,
+ 'line' => __LINE__,
+ 'message' => 'Invalid skin name'
+ ), true, false);
+
+ return false;
+ }
+
$valid = false;
$path = RCUBE_INSTALL_PATH . 'skins/';
@@ -210,11 +273,14 @@
$meta = @file_get_contents(RCUBE_INSTALL_PATH . $skin_path . '/meta.json');
$meta = @json_decode($meta, true);
- $meta['path'] = $skin_path;
- $skin_id = end(explode('/', $skin_path));
+ $meta['path'] = $skin_path;
+ $path_elements = explode('/', $skin_path);
+ $skin_id = end($path_elements);
+
if (!$meta['name']) {
$meta['name'] = $skin_id;
}
+
$this->skins[$skin_id] = $meta;
if ($meta['extends']) {
@@ -251,6 +317,7 @@
* @param string File name/path to resolve (starting with /)
* @param string Reference to the base path of the matching skin
* @param string Additional path to search in
+ *
* @return mixed Relative path to the requested file or False if not found
*/
public function get_skin_file($file, &$skin_path = null, $add_path = null)
@@ -261,9 +328,18 @@
}
foreach ($skin_paths as $skin_path) {
- $path = realpath($skin_path . $file);
- if (is_file($path)) {
+ $path = realpath(RCUBE_INSTALL_PATH . $skin_path . $file);
+
+ if ($path && is_file($path)) {
return $skin_path . $file;
+ }
+
+ if ($this->assets_dir != RCUBE_INSTALL_PATH) {
+ $path = realpath($this->assets_dir . $skin_path . $file);
+
+ if ($path && is_file($path)) {
+ return $skin_path . $file;
+ }
}
}
@@ -326,7 +402,7 @@
if ($override || !$this->message) {
if ($this->app->text_exists($message)) {
if (!empty($vars))
- $vars = array_map('Q', $vars);
+ $vars = array_map(array('rcube','Q'), $vars);
$msgtext = $this->app->gettext(array('name' => $message, 'vars' => $vars));
}
else
@@ -369,14 +445,15 @@
/**
* Redirect to a certain url
*
- * @param mixed $p Either a string with the action or url parameters as key-value pairs
- * @param int $delay Delay in seconds
+ * @param mixed $p Either a string with the action or url parameters as key-value pairs
+ * @param int $delay Delay in seconds
+ * @param bool $secure Redirect to secure location (see rcmail::url())
*/
- public function redirect($p = array(), $delay = 1)
+ public function redirect($p = array(), $delay = 1, $secure = false)
{
if ($this->env['extwin'])
$p['extwin'] = 1;
- $location = $this->app->url($p);
+ $location = $this->app->url($p, false, false, $secure);
header('Location: ' . $location);
exit;
}
@@ -439,10 +516,10 @@
// write all javascript commands
$this->add_script($commands, 'head_top');
- // send clickjacking protection headers
+ // allow (legal) iframe content to be loaded
$iframe = $this->framed || $this->env['framed'];
- if (!headers_sent() && ($xframe = $this->app->config->get('x_frame_options', 'sameorigin'))) {
- header('X-Frame-Options: ' . ($iframe && $xframe == 'deny' ? 'sameorigin' : $xframe));
+ if (!headers_sent() && $iframe && $this->app->config->get('x_frame_options', 'sameorigin') === 'deny') {
+ header('X-Frame-Options: sameorigin', true);
}
// call super method
@@ -490,11 +567,11 @@
// find skin template
$path = false;
foreach ($this->skin_paths as $skin_path) {
- $path = "$skin_path/templates/$name.html";
+ $path = RCUBE_INSTALL_PATH . "$skin_path/templates/$name.html";
// fallback to deprecated template names
if (!is_readable($path) && $this->deprecated_templates[$realname]) {
- $path = "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html";
+ $path = RCUBE_INSTALL_PATH . "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html";
if (is_readable($path)) {
rcube::raise_error(array(
@@ -520,7 +597,7 @@
// read template file
if (!$path || ($templ = @file_get_contents($path)) === false) {
rcube::raise_error(array(
- 'code' => 501,
+ 'code' => 404,
'type' => 'php',
'line' => __LINE__,
'file' => __FILE__,
@@ -667,6 +744,21 @@
exit;
}
+ /**
+ * Modify path by adding URL prefix if configured
+ */
+ public function asset_url($path)
+ {
+ // iframe content can't be in a different domain
+ // @TODO: check if assests are on a different domain
+
+ if (!$this->assets_path || in_array($path[0], array('?', '/', '.')) || strpos($path, '://')) {
+ return $path;
+ }
+
+ return $this->assets_path . $path;
+ }
+
/***** Template parsing methods *****/
@@ -704,7 +796,7 @@
}
/**
- * Callback function for preg_replace_callback in write()
+ * Callback function for preg_replace_callback in fix_paths()
*
* @return string Parsed string
*/
@@ -727,6 +819,28 @@
}
/**
+ * Correct paths of asset files according to assets_path
+ */
+ protected function fix_assets_paths($output)
+ {
+ return preg_replace_callback(
+ '!(src|href|background)=(["\']?)([a-z0-9/_.?=-]+)(["\'\s>])!i',
+ array($this, 'assets_callback'), $output);
+ }
+
+ /**
+ * Callback function for preg_replace_callback in fix_assets_paths()
+ *
+ * @return string Parsed string
+ */
+ protected function assets_callback($matches)
+ {
+ $file = $this->asset_url($matches[3]);
+
+ return $matches[1] . '=' . $matches[2] . $file . $matches[4];
+ }
+
+ /**
* Modify file by adding mtime indicator
*/
protected function file_mod($file)
@@ -737,12 +851,12 @@
// use minified file if exists (not in development mode)
if (!$this->devel_mode && !preg_match('/\.min\.' . $ext . '$/', $file)) {
$minified_file = substr($file, 0, strlen($ext) * -1) . 'min.' . $ext;
- if ($fs = @filemtime($minified_file)) {
+ if ($fs = @filemtime($this->assets_dir . $minified_file)) {
return $minified_file . '?s=' . $fs;
}
}
- if ($fs = @filemtime($file)) {
+ if ($fs = @filemtime($this->assets_dir . $file)) {
$file .= '?s=' . $fs;
}
@@ -856,7 +970,7 @@
"rcube_utils::get_input_value('\\1', rcube_utils::INPUT_GPC)",
"\$_COOKIE['\\1']",
"\$browser->{'\\1'}",
- $this->template_name,
+ "'" . $this->template_name . "'",
),
$expression
);
@@ -969,7 +1083,7 @@
if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path'];
if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) {
$this->base_path = preg_replace('!plugins/\w+/!', '', $skin_path); // set base_path to core skin directory (not plugin's skin)
- $path = realpath($path);
+ $path = realpath(RCUBE_INSTALL_PATH . $path);
}
if (is_readable($path)) {
@@ -1156,7 +1270,6 @@
if (!($attrib['command'] || $attrib['name'] || $attrib['href'])) {
return '';
}
-
// try to find out the button type
if ($attrib['type']) {
@@ -1521,6 +1634,10 @@
$output = $this->parse_with_globals($this->fix_paths($output));
+ if ($this->assets_path) {
+ $output = $this->fix_assets_paths($output);
+ }
+
// trigger hook with final HTML content to be sent
$hook = $this->app->plugins->exec_hook("send_page", array('content' => $output));
if (!$hook['abort']) {
@@ -1549,12 +1666,12 @@
}
$attrib['name'] = $attrib['id'];
- $attrib['src'] = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif';
+ $attrib['src'] = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif';
// register as 'contentframe' object
if ($is_contentframe || $attrib['contentframe']) {
$this->set_env('contentframe', $attrib['contentframe'] ? $attrib['contentframe'] : $attrib['name']);
- $this->set_env('blankpage', $attrib['src']);
+ $this->set_env('blankpage', $this->asset_url($attrib['src']));
}
return html::iframe($attrib);
@@ -1766,9 +1883,11 @@
{
$images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY);
$images = array_map(array($this, 'abs_url'), $images);
+ $images = array_map(array($this, 'asset_url'), $images);
- if (empty($images) || $this->app->task == 'logout')
+ if (empty($images) || $_REQUEST['_task'] == 'logout') {
return;
+ }
$this->add_script('var images = ' . self::json_serialize($images) .';
for (var i=0; i<images.length; i++) {
@@ -1928,5 +2047,4 @@
return $content;
}
-
}
--
Gitblit v1.9.1