0
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2024-09-19 19:22:14 +02:00
postfixadmin/model/PFASmarty.php

157 lines
5.5 KiB
PHP
Raw Permalink Normal View History

2020-09-27 11:52:19 +02:00
<?php
/**
* Turn on sanitisation of all data by default so it's not possible for XSS flaws to occur in PFA
*/
2021-03-22 10:28:28 +01:00
class PFASmarty
{
2020-09-27 11:52:19 +02:00
public static $instance = null;
2020-09-28 21:33:21 +02:00
/**
* @var Smarty
*/
protected $template;
2020-09-27 11:52:19 +02:00
2021-04-13 22:19:16 +02:00
public static function getInstance()
{
2020-09-27 11:52:19 +02:00
if (self::$instance) {
return self::$instance;
}
2020-09-28 21:33:21 +02:00
self::$instance = new PFASmarty();
2020-09-27 11:52:19 +02:00
return self::$instance;
}
2020-09-28 21:33:21 +02:00
2021-04-13 22:19:16 +02:00
private function __construct()
{
2020-09-27 11:52:19 +02:00
$CONF = Config::getInstance()->getAll();
2020-09-28 21:33:21 +02:00
$theme = '';
2020-09-27 11:52:19 +02:00
if (isset($CONF['theme']) && is_dir(dirname(__FILE__) . "/../templates/" . $CONF['theme'])) {
2020-09-28 21:33:21 +02:00
$theme = $CONF['theme'];
2020-09-27 11:52:19 +02:00
}
2020-09-28 21:33:21 +02:00
$this->template = new Smarty();
2020-09-27 11:52:19 +02:00
2020-09-28 21:33:21 +02:00
$template_dir = __DIR__ . '/../templates/' . $theme;
2020-09-27 11:52:19 +02:00
2020-09-28 21:33:21 +02:00
if (!is_dir($template_dir)) {
$template_dir = __DIR__ . '/../templates/';
}
$this->template->setTemplateDir($template_dir);
2020-09-27 11:52:19 +02:00
2020-09-28 21:33:21 +02:00
// if it's not present or writeable, smarty should just not cache.
$templates_c = dirname(__FILE__) . '/../templates_c';
if (is_dir($templates_c) && is_writeable($templates_c)) {
$this->template->setCompileDir($templates_c);
} else {
# unfortunately there's no sane way to just disable compiling of templates
clearstatcache(); // just incase someone just fixed it; on their next refresh it should work.
error_log("ERROR: directory $templates_c doesn't exist or isn't writeable for the webserver");
die("ERROR: the templates_c directory doesn't exist or isn't writeable for the webserver");
}
2020-09-27 11:52:19 +02:00
2020-09-28 21:33:21 +02:00
$this->configureTheme('');// default to something.
2020-09-27 11:52:19 +02:00
}
/**
* @param string $rel_path - relative path for referenced css etc dependencies - e.g. users/edit.php needs '../' else, it's ''.
*/
2021-04-13 22:19:16 +02:00
public function configureTheme(string $rel_path = '')
{
2020-09-27 11:52:19 +02:00
$CONF = Config::getInstance()->getAll();
// see: https://github.com/postfixadmin/postfixadmin/issues/410
// ignore $CONF['theme_css'] if it points to css/default.css and we have css/bootstrap.css.
if ($CONF['theme_css'] == 'css/default.css' && is_file(__DIR__ . '/../public/css/bootstrap.css')) {
// silently upgrade to bootstrap, css/default.css does not exist.
$CONF['theme_css'] = 'css/bootstrap.css';
}
2020-09-27 11:52:19 +02:00
$CONF['theme_css'] = $rel_path . htmlentities($CONF['theme_css']);
if (!empty($CONF['theme_custom_css'])) {
$CONF['theme_custom_css'] = $rel_path . htmlentities($CONF['theme_custom_css']);
}
if (array_key_exists('theme_favicon', $CONF)) {
$CONF['theme_favicon'] = $rel_path . htmlentities($CONF['theme_favicon']);
}
2020-09-27 11:52:19 +02:00
$CONF['theme_logo'] = $rel_path . htmlentities($CONF['theme_logo']);
$this->assign('rel_path', $rel_path);
2020-09-27 11:52:19 +02:00
$this->assign('CONF', $CONF);
}
/**
* @param string $key
* @param mixed $value
* @param bool $sanitise
*/
2021-04-13 22:19:16 +02:00
public function assign($key, $value, $sanitise = true)
{
2020-09-27 11:52:19 +02:00
$this->template->assign("RAW_$key", $value);
if ($sanitise == false) {
return $this->template->assign($key, $value);
}
$clean = $this->sanitise($value);
/* we won't run the key through sanitise() here... some might argue we should */
return $this->template->assign($key, $clean);
}
/**
* @param string $template
* @return void
*/
2021-04-13 22:19:16 +02:00
public function display($template)
{
2020-09-28 21:33:21 +02:00
$CONF = Config::getInstance()->getAll();
$this->assign('PALANG', $CONF['__LANG'] ?? []);
$this->assign('url_domain', '');
$this->assign('version', $CONF['version'] ?? 'unknown');
$this->assign('boolconf_alias_domain', Config::bool('alias_domain'));
2022-06-21 01:55:36 +02:00
$this->assign('boolconf_dkim', Config::bool('dkim'));
$this->assign('boolconf_dkim_all_admins', Config::bool('dkim_all_admins'));
2020-09-28 21:33:21 +02:00
$this->assign('authentication_has_role', array('global_admin' => authentication_has_role('global-admin'), 'admin' => authentication_has_role('admin'), 'user' => authentication_has_role('user')));
2020-09-27 11:52:19 +02:00
header("Expires: Sun, 16 Mar 2003 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Content-Type: text/html; charset=UTF-8");
2020-09-28 21:33:21 +02:00
$this->template->setConfigDir(__DIR__ . '/../configs');
2020-09-27 11:52:19 +02:00
$this->template->display($template);
2020-09-28 21:33:21 +02:00
2020-09-27 11:52:19 +02:00
unset($_SESSION['flash']); # cleanup flash messages
}
/**
* Recursive cleaning of data, using htmlentities - this assumes we only ever output to HTML and we're outputting in UTF-8 charset
*
* @param mixed $data - array or primitive type; objects not supported.
* @return mixed $data
* */
2021-04-13 22:19:16 +02:00
public function sanitise($data)
{
if (!is_array($data) && !is_string($data)) {
return $data; // bool, int, null, object etc - can't sanitise.
2021-01-26 21:42:28 +01:00
}
if (is_string($data)) {
2020-09-27 11:52:19 +02:00
return htmlentities($data, ENT_QUOTES, 'UTF-8', false);
}
2020-09-27 11:52:19 +02:00
$clean = array();
foreach ($data as $key => $value) {
/* as this is a nested data structure it's more likely we'll output the key too (at least in my opinion, so we'll sanitise it too */
$clean[$this->sanitise($key)] = $this->sanitise($value);
}
return $clean;
}
}