major refactoring of almost all files

This commit is contained in:
envoyr
2022-04-28 20:48:00 +02:00
parent a2e95b960f
commit 4f4c71d79b
285 changed files with 21716 additions and 18766 deletions

View File

@@ -1,38 +1,48 @@
<?php
namespace Froxlor\Database;
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* @since 0.9.31
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Database;
use Exception;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\Settings;
use Froxlor\UI\Panel\UI;
use PDO;
use PDOException;
use PDOStatement;
/**
* Class Database
*
* Wrapper-class for PHP-PDO
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
*
* @method static \PDOStatement prepare($statement, array $driver_options = null) Prepares a statement for execution and returns a statement object
* @method static \PDOStatement query ($statement) Executes an SQL statement, returning a result set as a PDOStatement object
* @method static PDOStatement prepare($statement, array $driver_options = null) Prepares a statement for execution
* and returns a statement object
* @method static PDOStatement query ($statement) Executes an SQL statement, returning a result set as a PDOStatement
* object
* @method static string lastInsertId ($name = null) Returns the ID of the last inserted row or sequence value
* @method static string quote ($string, $parameter_type = null) Quotes a string for use in a query.
*/
@@ -68,42 +78,233 @@ class Database
private static $sqldata = null;
/**
* Wrapper for PDOStatement::execute so we can catch the PDOException
* and display the error nicely on the panel
*
* @param \PDOStatement $stmt
* @param array $params
* (optional)
* @param bool $showerror
* suppress errordisplay (default true)
*/
public static function pexecute(&$stmt, $params = null, $showerror = true, $json_response = false)
{
try {
$stmt->execute($params);
} catch (\PDOException $e) {
self::showerror($e, $showerror, $json_response, $stmt);
}
}
/**
* Wrapper for PDOStatement::execute so we can catch the PDOException
* and display the error nicely on the panel - also fetches the
* result from the statement and returns the resulting array
*
* @param \PDOStatement $stmt
* @param PDOStatement $stmt
* @param array $params
* (optional)
* (optional)
* @param bool $showerror
* suppress errordisplay (default true)
* suppress errordisplay (default true)
*
* @return array
*/
public static function pexecute_first(&$stmt, $params = null, $showerror = true, $json_response = false)
{
self::pexecute($stmt, $params, $showerror, $json_response);
return $stmt->fetch(\PDO::FETCH_ASSOC);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* Wrapper for PDOStatement::execute so we can catch the PDOException
* and display the error nicely on the panel
*
* @param PDOStatement $stmt
* @param array $params
* (optional)
* @param bool $showerror
* suppress errordisplay (default true)
*/
public static function pexecute(&$stmt, $params = null, $showerror = true, $json_response = false)
{
try {
$stmt->execute($params);
} catch (PDOException $e) {
self::showerror($e, $showerror, $json_response, $stmt);
}
}
/**
* display a nice error if it occurs and log everything
*
* @param PDOException $error
* @param bool $showerror
* if set to false, the error will be logged but we go on
*/
private static function showerror($error, $showerror = true, $json_response = false, PDOStatement $stmt = null)
{
global $userinfo, $theme, $linker;
// include userdata.inc.php
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
// le format
if (isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
$sql_root = [
0 => [
'caption' => 'Default',
'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
'user' => $sql['root_user'],
'password' => $sql['root_password']
]
];
}
$substitutions = [
$sql['password'] => 'DB_UNPRIV_PWD',
$sql_root[0]['password'] => 'DB_ROOT_PWD'
];
// hide username/password in messages
$error_message = $error->getMessage();
$error_trace = $error->getTraceAsString();
// error-message
$error_message = self::substitute($error_message, $substitutions);
// error-trace
$error_trace = self::substitute($error_trace, $substitutions);
if ($error->getCode() == 2003) {
$error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong.";
$error_trace = "";
}
/**
* log to a file, so we can actually ask people for the error
* (no one seems to find the stuff in the syslog)
*/
$sl_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . "/logs/");
if (!file_exists($sl_dir)) {
@mkdir($sl_dir, 0755);
}
openlog("froxlor", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, str_replace("\n", " ", $error_message));
syslog(LOG_WARNING, str_replace("\n", " ", "--- DEBUG: " . $error_trace));
closelog();
/**
* log error for reporting
*/
$errid = self::genUniqueToken();
$err_file = FileDir::makeCorrectFile($sl_dir . "/" . $errid . "_sql-error.log");
$errlog = @fopen($err_file, 'w');
@fwrite($errlog, "|CODE " . $error->getCode() . "\n");
@fwrite($errlog, "|MSG " . $error_message . "\n");
@fwrite($errlog, "|FILE " . $error->getFile() . "\n");
@fwrite($errlog, "|LINE " . $error->getLine() . "\n");
@fwrite($errlog, "|TRACE\n" . $error_trace . "\n");
@fclose($errlog);
if (empty($sql['debug'])) {
$error_trace = '';
} elseif (!is_null($stmt)) {
$error_trace .= "\n\n" . $stmt->queryString;
}
if ($showerror && $json_response) {
$exception_message = $error_message;
if (isset($sql['debug']) && $sql['debug'] == true) {
$exception_message .= "\n\n" . $error_trace;
}
throw new Exception($exception_message, 500);
}
if ($showerror) {
// clean up sensitive data
unset($sql);
unset($sql_root);
if ((isset($theme) && $theme != '') && !isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {
// if we're not on the shell, output a nice error
$err_report_link = '';
if (is_array($userinfo) && (($userinfo['adminsession'] == '1' && Settings::Get('system.allow_error_report_admin') == '1') || ($userinfo['adminsession'] == '0' && Settings::Get('system.allow_error_report_customer') == '1'))) {
$err_report_link = $linker->getLink([
'section' => 'index',
'page' => 'send_error_report',
'errorid' => $errid
]);
}
// show
UI::initTwig(true);
UI::twig()->addGlobal('install_mode', '1');
UI::view('misc/dberrornice.html.twig', [
'page_title' => 'Database error',
'message' => $error_message,
'debug' => $error_trace,
'report' => $err_report_link
]);
die();
}
die("We are sorry, but a MySQL - error occurred. The administrator may find more information in the syslog");
}
}
/**
* Substitutes patterns in content.
*
* @param string $content
* @param array $substitutions
* @param int $minLength
* @return string
*/
private static function substitute($content, array $substitutions, $minLength = 6)
{
$replacements = [];
foreach ($substitutions as $search => $replace) {
$replacements = $replacements + self::createShiftedSubstitutions($search, $replace, $minLength);
}
$content = str_replace(array_keys($replacements), array_values($replacements), $content);
return $content;
}
/**
* Creates substitutions, shifted by length, e.g.
*
* _createShiftedSubstitutions('abcdefgh', 'value', 4):
* array(
* 'abcdefgh' => 'value',
* 'abcdefg' => 'value',
* 'abcdef' => 'value',
* 'abcde' => 'value',
* 'abcd' => 'value',
* )
*
* @param string $search
* @param string $replace
* @param int $minLength
* @return array
*/
private static function createShiftedSubstitutions($search, $replace, $minLength)
{
$substitutions = [];
$length = strlen($search);
if ($length > $minLength) {
for ($shiftedLength = $length; $shiftedLength >= $minLength; $shiftedLength--) {
$substitutions[substr($search, 0, $shiftedLength)] = $replace;
}
}
return $substitutions;
}
/**
* generate safe unique token
*
* @param int $length
* @return string
*/
private static function genUniqueToken(int $length = 16)
{
if (!isset($length) || intval($length) <= 8) {
$length = 16;
}
if (function_exists('random_bytes')) {
return bin2hex(random_bytes($length));
}
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
}
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes($length));
}
// if everything else fails, use unsafe fallback
return substr(md5(uniqid(microtime(), 1)), 0, $length);
}
/**
@@ -134,7 +335,7 @@ class Database
*
* @param bool $needroot
* @param int $dbserver
* optional
* optional
*/
public static function needRoot($needroot = false, $dbserver = 0)
{
@@ -144,6 +345,17 @@ class Database
self::$needroot = $needroot;
}
/**
* set the database-server (relevant for root-connection)
*
* @param int $dbserver
*/
private static function setServer($dbserver = 0)
{
self::$dbserver = $dbserver;
self::$link = null;
}
/**
* enable the temporary access to sql-access data
* note: if you want root-sqldata you need to
@@ -155,7 +367,7 @@ class Database
public static function needSqlData()
{
self::$needsqldata = true;
self::$sqldata = array();
self::$sqldata = [];
self::$link = null;
// we need a connection here because
// if getSqlData() is called RIGHT after
@@ -165,6 +377,140 @@ class Database
self::getDB();
}
/**
* function that will be called on every static call
* which connects to the database if necessary
*
* @param bool $root
*
* @return object
*/
private static function getDB()
{
if (!extension_loaded('pdo') || in_array("mysql", PDO::getAvailableDrivers()) == false) {
self::showerror(new Exception("The php PDO extension or PDO-MySQL driver is not available"));
}
// do we got a connection already?
if (self::$link) {
// return it
return self::$link;
}
// include userdata.inc.php
require Froxlor::getInstallDir() . "/lib/userdata.inc.php";
// le format
if (self::$needroot == true && isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
$sql_root = [
0 => [
'caption' => 'Default',
'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
'user' => $sql['root_user'],
'password' => $sql['root_password']
]
];
unset($sql['root_user']);
unset($sql['root_password']);
}
// either root or unprivileged user
if (self::$needroot) {
$caption = $sql_root[self::$dbserver]['caption'];
$user = $sql_root[self::$dbserver]['user'];
$password = $sql_root[self::$dbserver]['password'];
$host = $sql_root[self::$dbserver]['host'];
$socket = isset($sql_root[self::$dbserver]['socket']) ? $sql_root[self::$dbserver]['socket'] : null;
$port = isset($sql_root[self::$dbserver]['port']) ? $sql_root[self::$dbserver]['port'] : '3306';
$sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false;
} else {
$caption = 'localhost';
$user = $sql["user"];
$password = $sql["password"];
$host = $sql["host"];
$socket = isset($sql['socket']) ? $sql['socket'] : null;
$port = isset($sql['port']) ? $sql['port'] : '3306';
$sslCAFile = $sql['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false;
}
// save sql-access-data if needed
if (self::$needsqldata) {
self::$sqldata = [
'user' => $user,
'passwd' => $password,
'host' => $host,
'port' => $port,
'socket' => $socket,
'db' => $sql["db"],
'caption' => $caption,
'ssl_ca_file' => $sslCAFile,
'ssl_verify_server_certificate' => $sslVerifyServerCertificate
];
}
// build up connection string
$driver = 'mysql';
$dsn = $driver . ":";
$options = [
'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'
];
$attributes = [
'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'
];
$dbconf["dsn"] = [
'dbname' => $sql["db"],
'charset' => 'utf8'
];
if ($socket != null) {
$dbconf["dsn"]['unix_socket'] = FileDir::makeCorrectFile($socket);
} else {
$dbconf["dsn"]['host'] = $host;
$dbconf["dsn"]['port'] = $port;
if (!empty(self::$sqldata['ssl_ca_file'])) {
$options[PDO::MYSQL_ATTR_SSL_CA] = self::$sqldata['ssl_ca_file'];
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)self::$sqldata['ssl_verify_server_certificate'];
}
}
self::$dbname = $sql["db"];
// add options to dsn-string
foreach ($dbconf["dsn"] as $k => $v) {
$dsn .= $k . "=" . $v . ";";
}
// clean up
unset($dbconf);
// try to connect
try {
self::$link = new PDO($dsn, $user, $password, $options);
} catch (PDOException $e) {
self::showerror($e);
}
// set attributes
foreach ($attributes as $k => $v) {
self::$link->setAttribute(constant("PDO::" . $k), constant("PDO::" . $v));
}
$version_server = self::$link->getAttribute(PDO::ATTR_SERVER_VERSION);
$sql_mode = 'NO_ENGINE_SUBSTITUTION';
if (version_compare($version_server, '8.0.11', '<')) {
$sql_mode .= ',NO_AUTO_CREATE_USER';
}
self::$link->exec('SET sql_mode = "' . $sql_mode . '"');
// return PDO instance
return self::$link;
}
/**
* returns the sql-access data as array using indices
* 'user', 'passwd' and 'host'.
@@ -193,7 +539,7 @@ class Database
{
// MySQL user names can be up to 32 characters long (16 characters before MySQL 5.7.8).
$mysql_max = 32;
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {
$mysql_max = 16;
}
return $mysql_max;
@@ -210,352 +556,16 @@ class Database
*/
public static function __callStatic($name, $args)
{
$callback = array(
$callback = [
self::getDB(),
$name
);
];
$result = null;
try {
$result = call_user_func_array($callback, $args);
} catch (\PDOException $e) {
} catch (PDOException $e) {
self::showerror($e);
}
return $result;
}
/**
* set the database-server (relevant for root-connection)
*
* @param int $dbserver
*/
private static function setServer($dbserver = 0)
{
self::$dbserver = $dbserver;
self::$link = null;
}
/**
* function that will be called on every static call
* which connects to the database if necessary
*
* @param bool $root
*
* @return object
*/
private static function getDB()
{
if (!extension_loaded('pdo') || in_array("mysql", \PDO::getAvailableDrivers()) == false) {
self::showerror(new \Exception("The php PDO extension or PDO-MySQL driver is not available"));
}
// do we got a connection already?
if (self::$link) {
// return it
return self::$link;
}
// include userdata.inc.php
require \Froxlor\Froxlor::getInstallDir() . "/lib/userdata.inc.php";
// le format
if (self::$needroot == true && isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
$sql_root = array(
0 => array(
'caption' => 'Default',
'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
'user' => $sql['root_user'],
'password' => $sql['root_password']
)
);
unset($sql['root_user']);
unset($sql['root_password']);
}
// either root or unprivileged user
if (self::$needroot) {
$caption = $sql_root[self::$dbserver]['caption'];
$user = $sql_root[self::$dbserver]['user'];
$password = $sql_root[self::$dbserver]['password'];
$host = $sql_root[self::$dbserver]['host'];
$socket = isset($sql_root[self::$dbserver]['socket']) ? $sql_root[self::$dbserver]['socket'] : null;
$port = isset($sql_root[self::$dbserver]['port']) ? $sql_root[self::$dbserver]['port'] : '3306';
$sslCAFile = $sql_root[self::$dbserver]['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql_root[self::$dbserver]['ssl']['verifyServerCertificate'] ?? false;
} else {
$caption = 'localhost';
$user = $sql["user"];
$password = $sql["password"];
$host = $sql["host"];
$socket = isset($sql['socket']) ? $sql['socket'] : null;
$port = isset($sql['port']) ? $sql['port'] : '3306';
$sslCAFile = $sql['ssl']['caFile'] ?? "";
$sslVerifyServerCertificate = $sql['ssl']['verifyServerCertificate'] ?? false;
}
// save sql-access-data if needed
if (self::$needsqldata) {
self::$sqldata = array(
'user' => $user,
'passwd' => $password,
'host' => $host,
'port' => $port,
'socket' => $socket,
'db' => $sql["db"],
'caption' => $caption,
'ssl_ca_file' => $sslCAFile,
'ssl_verify_server_certificate' => $sslVerifyServerCertificate
);
}
// build up connection string
$driver = 'mysql';
$dsn = $driver . ":";
$options = array(
'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8'
);
$attributes = array(
'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION'
);
$dbconf["dsn"] = array(
'dbname' => $sql["db"],
'charset' => 'utf8'
);
if ($socket != null) {
$dbconf["dsn"]['unix_socket'] = \Froxlor\FileDir::makeCorrectFile($socket);
} else {
$dbconf["dsn"]['host'] = $host;
$dbconf["dsn"]['port'] = $port;
if (!empty(self::$sqldata['ssl_ca_file'])) {
$options[\PDO::MYSQL_ATTR_SSL_CA] = self::$sqldata['ssl_ca_file'];
$options[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) self::$sqldata['ssl_verify_server_certificate'];
}
}
self::$dbname = $sql["db"];
// add options to dsn-string
foreach ($dbconf["dsn"] as $k => $v) {
$dsn .= $k . "=" . $v . ";";
}
// clean up
unset($dbconf);
// try to connect
try {
self::$link = new \PDO($dsn, $user, $password, $options);
} catch (\PDOException $e) {
self::showerror($e);
}
// set attributes
foreach ($attributes as $k => $v) {
self::$link->setAttribute(constant("PDO::" . $k), constant("PDO::" . $v));
}
$version_server = self::$link->getAttribute(\PDO::ATTR_SERVER_VERSION);
$sql_mode = 'NO_ENGINE_SUBSTITUTION';
if (version_compare($version_server, '8.0.11', '<')) {
$sql_mode .= ',NO_AUTO_CREATE_USER';
}
self::$link->exec('SET sql_mode = "' . $sql_mode . '"');
// return PDO instance
return self::$link;
}
/**
* display a nice error if it occurs and log everything
*
* @param \PDOException $error
* @param bool $showerror
* if set to false, the error will be logged but we go on
*/
private static function showerror($error, $showerror = true, $json_response = false, \PDOStatement $stmt = null)
{
global $userinfo, $theme, $linker;
// include userdata.inc.php
require \Froxlor\Froxlor::getInstallDir() . "/lib/userdata.inc.php";
// le format
if (isset($sql['root_user']) && isset($sql['root_password']) && (!isset($sql_root) || !is_array($sql_root))) {
$sql_root = array(
0 => array(
'caption' => 'Default',
'host' => $sql['host'],
'socket' => (isset($sql['socket']) ? $sql['socket'] : null),
'user' => $sql['root_user'],
'password' => $sql['root_password']
)
);
}
$substitutions = array(
$sql['password'] => 'DB_UNPRIV_PWD',
$sql_root[0]['password'] => 'DB_ROOT_PWD'
);
// hide username/password in messages
$error_message = $error->getMessage();
$error_trace = $error->getTraceAsString();
// error-message
$error_message = self::substitute($error_message, $substitutions);
// error-trace
$error_trace = self::substitute($error_trace, $substitutions);
if ($error->getCode() == 2003) {
$error_message = "Unable to connect to database. Either the mysql-server is not running or your user/password is wrong.";
$error_trace = "";
}
/**
* log to a file, so we can actually ask people for the error
* (no one seems to find the stuff in the syslog)
*/
$sl_dir = \Froxlor\FileDir::makeCorrectDir(\Froxlor\Froxlor::getInstallDir() . "/logs/");
if (!file_exists($sl_dir)) {
@mkdir($sl_dir, 0755);
}
openlog("froxlor", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, str_replace("\n", " ", $error_message));
syslog(LOG_WARNING, str_replace("\n", " ", "--- DEBUG: " . $error_trace));
closelog();
/**
* log error for reporting
*/
$errid = self::genUniqueToken();
$err_file = \Froxlor\FileDir::makeCorrectFile($sl_dir . "/" . $errid . "_sql-error.log");
$errlog = @fopen($err_file, 'w');
@fwrite($errlog, "|CODE " . $error->getCode() . "\n");
@fwrite($errlog, "|MSG " . $error_message . "\n");
@fwrite($errlog, "|FILE " . $error->getFile() . "\n");
@fwrite($errlog, "|LINE " . $error->getLine() . "\n");
@fwrite($errlog, "|TRACE\n" . $error_trace . "\n");
@fclose($errlog);
if (empty($sql['debug'])) {
$error_trace = '';
} elseif (!is_null($stmt)) {
$error_trace .= "\n\n" . $stmt->queryString;
}
if ($showerror && $json_response) {
$exception_message = $error_message;
if (isset($sql['debug']) && $sql['debug'] == true) {
$exception_message .= "\n\n" . $error_trace;
}
throw new \Exception($exception_message, 500);
}
if ($showerror) {
// clean up sensitive data
unset($sql);
unset($sql_root);
if ((isset($theme) && $theme != '') && !isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {
// if we're not on the shell, output a nice error
$err_report_link = '';
if (is_array($userinfo) && (($userinfo['adminsession'] == '1' && \Froxlor\Settings::Get('system.allow_error_report_admin') == '1') || ($userinfo['adminsession'] == '0' && \Froxlor\Settings::Get('system.allow_error_report_customer') == '1'))) {
$err_report_link = $linker->getLink(array(
'section' => 'index',
'page' => 'send_error_report',
'errorid' => $errid
));
}
// show
\Froxlor\UI\Panel\UI::initTwig(true);
\Froxlor\UI\Panel\UI::twig()->addGlobal('install_mode', '1');
\Froxlor\UI\Panel\UI::view('misc/dberrornice.html.twig', [
'page_title' => 'Database error',
'message' => $error_message,
'debug' => $error_trace,
'report' => $err_report_link
]);
die();
}
die("We are sorry, but a MySQL - error occurred. The administrator may find more information in the syslog");
}
}
/**
* generate safe unique token
*
* @param int $length
* @return string
*/
private static function genUniqueToken(int $length = 16)
{
if (!isset($length) || intval($length) <= 8) {
$length = 16;
}
if (function_exists('random_bytes')) {
return bin2hex(random_bytes($length));
}
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
}
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes($length));
}
// if everything else fails, use unsafe fallback
return substr(md5(uniqid(microtime(), 1)), 0, $length);
}
/**
* Substitutes patterns in content.
*
* @param string $content
* @param array $substitutions
* @param int $minLength
* @return string
*/
private static function substitute($content, array $substitutions, $minLength = 6)
{
$replacements = array();
foreach ($substitutions as $search => $replace) {
$replacements = $replacements + self::createShiftedSubstitutions($search, $replace, $minLength);
}
$content = str_replace(array_keys($replacements), array_values($replacements), $content);
return $content;
}
/**
* Creates substitutions, shifted by length, e.g.
*
* _createShiftedSubstitutions('abcdefgh', 'value', 4):
* array(
* 'abcdefgh' => 'value',
* 'abcdefg' => 'value',
* 'abcdef' => 'value',
* 'abcde' => 'value',
* 'abcd' => 'value',
* )
*
* @param string $search
* @param string $replace
* @param int $minLength
* @return array
*/
private static function createShiftedSubstitutions($search, $replace, $minLength)
{
$substitutions = array();
$length = strlen($search);
if ($length > $minLength) {
for ($shiftedLength = $length; $shiftedLength >= $minLength; $shiftedLength--) {
$substitutions[substr($search, 0, $shiftedLength)] = $replace;
}
}
return $substitutions;
}
}

View File

@@ -1,37 +1,41 @@
<?php
namespace Froxlor\Database;
use Froxlor\Settings;
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* @since 0.9.31
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Database;
use Froxlor\Database\Manager\DbManagerMySQL;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger;
use Froxlor\Settings;
use PDO;
/**
* Class DbManager
*
* Wrapper-class for database-management like creating
* and removing databases, users and permissions
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
*/
class DbManager
{
@@ -53,7 +57,7 @@ class DbManager
/**
* main constructor
*
* @param \Froxlor\FroxlorLogger $log
* @param FroxlorLogger $log
*/
public function __construct($log = null)
{
@@ -61,6 +65,75 @@ class DbManager
$this->setManager();
}
/**
* set manager-object by type of
* dbms: mysql only for now
*
* sets private $_manager variable
*/
private function setManager()
{
// TODO read different dbms from settings later
$this->manager = new DbManagerMySQL($this->log);
}
public static function correctMysqlUsers($mysql_access_host_array)
{
// get sql-root access data
Database::needRoot(true);
Database::needSqlData();
$sql_root = Database::getSqlData();
Database::needRoot(false);
$dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`");
while ($dbserver = $dbservers_stmt->fetch(PDO::FETCH_ASSOC)) {
Database::needRoot(true, $dbserver['dbserver']);
Database::needSqlData();
$sql_root = Database::getSqlData();
$dbm = new DbManager(FroxlorLogger::getInstanceOf());
$users = $dbm->getManager()->getAllSqlUsers(false);
$databases = [
$sql_root['db']
];
$databases_result_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DATABASES . "`
WHERE `dbserver` = :mysqlserver
");
Database::pexecute($databases_result_stmt, [
'mysqlserver' => $dbserver['dbserver']
]);
while ($databases_row = $databases_result_stmt->fetch(PDO::FETCH_ASSOC)) {
$databases[] = $databases_row['databasename'];
}
foreach ($databases as $username) {
if (isset($users[$username]) && is_array($users[$username]) && isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {
$password = $users[$username]['password'];
foreach ($mysql_access_host_array as $mysql_access_host) {
$mysql_access_host = trim($mysql_access_host);
if (!in_array($mysql_access_host, $users[$username]['hosts'])) {
$dbm->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host, true);
}
}
foreach ($users[$username]['hosts'] as $mysql_access_host) {
if (!in_array($mysql_access_host, $mysql_access_host_array)) {
$dbm->getManager()->deleteUser($username, $mysql_access_host);
}
}
}
}
$dbm->getManager()->flushPrivileges();
Database::needRoot(false);
}
}
/**
* creates a new database and a user with the
* same name with all privileges granted on the db.
@@ -82,10 +155,10 @@ class DbManager
// get all usernames from db-manager
$allsqlusers = $this->getManager()->getAllSqlUsers();
// generate random username
$username = $loginname . '-' . substr(\Froxlor\Froxlor::genSessionId(), 20, 3);
$username = $loginname . '-' . substr(Froxlor::genSessionId(), 20, 3);
// check whether it exists on the DBMS
while (in_array($username, $allsqlusers)) {
$username = $loginname . '-' . substr(\Froxlor\Froxlor::genSessionId(), 20, 3);
$username = $loginname . '-' . substr(Froxlor::genSessionId(), 20, 3);
}
} elseif (strtoupper(Settings::Get('customer.mysqlprefix')) == 'DBNAME') {
$username = $loginname;
@@ -100,12 +173,12 @@ class DbManager
// now create the database itself
$this->getManager()->createDatabase($username);
$this->log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "created database '" . $username . "'");
// and give permission to the user on every access-host we have
foreach (array_map('trim', explode(',', Settings::Get('system.mysql_access_host'))) as $mysql_access_host) {
$this->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host);
$this->log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "grant all privileges for '" . $username . "'@'" . $mysql_access_host . "'");
$this->log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "grant all privileges for '" . $username . "'@'" . $mysql_access_host . "'");
}
$this->getManager()->flushPrivileges();
@@ -123,78 +196,4 @@ class DbManager
{
return $this->manager;
}
/**
* set manager-object by type of
* dbms: mysql only for now
*
* sets private $_manager variable
*/
private function setManager()
{
// TODO read different dbms from settings later
$this->manager = new \Froxlor\Database\Manager\DbManagerMySQL($this->log);
}
public static function correctMysqlUsers($mysql_access_host_array)
{
// get sql-root access data
Database::needRoot(true);
Database::needSqlData();
$sql_root = Database::getSqlData();
Database::needRoot(false);
$dbservers_stmt = Database::query("SELECT DISTINCT `dbserver` FROM `" . TABLE_PANEL_DATABASES . "`");
while ($dbserver = $dbservers_stmt->fetch(\PDO::FETCH_ASSOC)) {
Database::needRoot(true, $dbserver['dbserver']);
Database::needSqlData();
$sql_root = Database::getSqlData();
$dbm = new DbManager(\Froxlor\FroxlorLogger::getInstanceOf());
$users = $dbm->getManager()->getAllSqlUsers(false);
$databases = array(
$sql_root['db']
);
$databases_result_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DATABASES . "`
WHERE `dbserver` = :mysqlserver
");
Database::pexecute($databases_result_stmt, array(
'mysqlserver' => $dbserver['dbserver']
));
while ($databases_row = $databases_result_stmt->fetch(\PDO::FETCH_ASSOC)) {
$databases[] = $databases_row['databasename'];
}
foreach ($databases as $username) {
if (isset($users[$username]) && is_array($users[$username]) && isset($users[$username]['hosts']) && is_array($users[$username]['hosts'])) {
$password = $users[$username]['password'];
foreach ($mysql_access_host_array as $mysql_access_host) {
$mysql_access_host = trim($mysql_access_host);
if (! in_array($mysql_access_host, $users[$username]['hosts'])) {
$dbm->getManager()->grantPrivilegesTo($username, $password, $mysql_access_host, true);
}
}
foreach ($users[$username]['hosts'] as $mysql_access_host) {
if (! in_array($mysql_access_host, $mysql_access_host_array)) {
$dbm->getManager()->deleteUser($username, $mysql_access_host);
}
}
}
}
$dbm->getManager()->flushPrivileges();
Database::needRoot(false);
}
}
}

View File

@@ -1,30 +1,39 @@
<?php
namespace Froxlor\Database;
use Froxlor\Settings;
/**
* This file is part of the Froxlor project.
* Copyright (c) 2003-2009 the SysCP Team (see authors).
* Copyright (c) 2014- the Froxlor Team (see authors).
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* @copyright (c) the authors
* @author Florian Aders <eleras@froxlor.org>
* @author Froxlor team <team@froxlor.org> (2014-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Integrity
*
* IntegrityCheck - class
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Database;
use Froxlor\FroxlorLogger;
use Froxlor\Settings;
use PDO;
class IntegrityCheck
{
// Store all available checks
public $available = array();
public $available = [];
// logger object
private $log = null;
@@ -36,12 +45,12 @@ class IntegrityCheck
public function __construct()
{
global $userinfo;
if (! isset($userinfo) || ! is_array($userinfo)) {
$userinfo = array(
if (!isset($userinfo) || !is_array($userinfo)) {
$userinfo = [
'loginname' => 'integrity-check'
);
];
}
$this->log = \Froxlor\FroxlorLogger::getInstanceOf($userinfo);
$this->log = FroxlorLogger::getInstanceOf($userinfo);
$this->available = get_class_methods($this);
unset($this->available[array_search('__construct', $this->available)]);
unset($this->available[array_search('checkAll', $this->available)]);
@@ -77,31 +86,30 @@ class IntegrityCheck
* check whether the froxlor database and its tables are in utf-8 character-set
*
* @param bool $fix
* fix db charset/collation if not utf8
*
* fix db charset/collation if not utf8
*
* @return boolean
*/
public function databaseCharset($fix = false)
{
// get characterset
$cs_stmt = Database::prepare('SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = :dbname');
$resp = Database::pexecute_first($cs_stmt, array(
$resp = Database::pexecute_first($cs_stmt, [
'dbname' => Database::getDbName()
));
]);
$charset = isset($resp['default_character_set_name']) ? $resp['default_character_set_name'] : null;
if (! empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "database charset seems to be different from UTF-8, integrity-check can fix that");
if (!empty($charset) && substr(strtolower($charset), 0, 4) != 'utf8') {
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "database charset seems to be different from UTF-8, integrity-check can fix that");
if ($fix) {
// fix database
Database::query('ALTER DATABASE `' . Database::getDbName() . '` CHARACTER SET utf8 COLLATE utf8_general_ci');
// fix all tables
$handle = Database::query('SHOW FULL TABLES WHERE Table_type != "VIEW"');
while ($row = $handle->fetch(\PDO::FETCH_BOTH)) {
while ($row = $handle->fetch(PDO::FETCH_BOTH)) {
$table = $row[0];
Database::query('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;');
}
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "database charset was different from UTF-8, integrity-check fixed that");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "database charset was different from UTF-8, integrity-check fixed that");
} else {
return false;
}
@@ -117,14 +125,14 @@ class IntegrityCheck
* Check the integrity of the domain to ip/port - association
*
* @param bool $fix
* Fix everything found directly
* Fix everything found directly
*/
public function domainIpTable($fix = false)
{
$ips = array();
$domains = array();
$ipstodomains = array();
$admips = array();
$ips = [];
$domains = [];
$ipstodomains = [];
$admips = [];
if ($fix) {
// Prepare insert / delete statement for the fixes
@@ -140,14 +148,14 @@ class IntegrityCheck
Database::pexecute($adm_stmt);
$default_ips = explode(',', Settings::Get('system.defaultip'));
$default_ssl_ips = explode(',', Settings::Get('system.defaultsslip'));
while ($row = $adm_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $adm_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($row['ip'] < 0 || is_null($row['ip']) || empty($row['ip'])) {
// Admin uses default-IP
$admips[$row['adminid']] = array_merge($default_ips, $default_ssl_ips);
} else {
$admips[$row['adminid']] = array(
$admips[$row['adminid']] = [
$row['ip']
);
];
}
}
}
@@ -155,42 +163,42 @@ class IntegrityCheck
// Cache all available ip/port - combinations
$result_stmt = Database::prepare("SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` ORDER BY `id` ASC");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$ips[$row['id']] = $row['ip'] . ':' . $row['port'];
}
// Cache all configured domains
$result_stmt = Database::prepare("SELECT `id`, `adminid` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `id` ASC");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$domains[$row['id']] = $row['adminid'];
}
// Check if every domain to ip/port - association is valid in TABLE_DOMAINTOIP
$result_stmt = Database::prepare("SELECT `id_domain`, `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "`");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
if (! array_key_exists($row['id_ipandports'], $ips)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if (!array_key_exists($row['id_ipandports'], $ips)) {
if ($fix) {
Database::pexecute($del_stmt, array(
Database::pexecute($del_stmt, [
'domainid' => $row['id_domain'],
'ipandportid' => $row['id_ipandports']
));
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this");
]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found an ip/port-id in domain <> ip table which does not exist, integrity check fixed this");
} else {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found an ip/port-id in domain <> ip table which does not exist, integrity check can fix this");
return false;
}
}
if (! array_key_exists($row['id_domain'], $domains)) {
if (!array_key_exists($row['id_domain'], $domains)) {
if ($fix) {
Database::pexecute($del_stmt, array(
Database::pexecute($del_stmt, [
'domainid' => $row['id_domain'],
'ipandportid' => $row['id_ipandports']
));
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id in domain <> ip table which does not exist, integrity check fixed this");
]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id in domain <> ip table which does not exist, integrity check fixed this");
} else {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id in domain <> ip table which does not exist, integrity check can fix this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id in domain <> ip table which does not exist, integrity check can fix this");
return false;
}
}
@@ -200,17 +208,17 @@ class IntegrityCheck
// Check that all domains have at least one IP/Port combination
foreach ($domains as $domainid => $adminid) {
if (! array_key_exists($domainid, $ipstodomains)) {
if (!array_key_exists($domainid, $ipstodomains)) {
if ($fix) {
foreach ($admips[$adminid] as $defaultip) {
Database::pexecute($ins_stmt, array(
Database::pexecute($ins_stmt, [
'domainid' => $domainid,
'ipandportid' => $defaultip
));
]);
}
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id with no entry in domain <> ip table, integrity check fixed this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a domain-id with no entry in domain <> ip table, integrity check fixed this");
} else {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id with no entry in domain <> ip table, integrity check can fix this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a domain-id with no entry in domain <> ip table, integrity check can fix this");
return false;
}
}
@@ -227,13 +235,13 @@ class IntegrityCheck
* Check if all subdomains have ssl-redirect = 0 if domain has no ssl-port
*
* @param bool $fix
* Fix everything found directly
* Fix everything found directly
*/
public function subdomainSslRedirect($fix = false)
{
$ips = array();
$parentdomains = array();
$subdomains = array();
$ips = [];
$parentdomains = [];
$subdomains = [];
if ($fix) {
// Prepare update statement for the fixes
@@ -245,7 +253,7 @@ class IntegrityCheck
// Cache all ssl ip/port - combinations
$result_stmt = Database::prepare("SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl` = 1 ORDER BY `id` ASC");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$ips[$row['id']] = $row['ip'] . ':' . $row['port'];
}
@@ -253,14 +261,14 @@ class IntegrityCheck
$result_stmt = Database::prepare("SELECT `id`, `parentdomainid`, `ssl_redirect` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `id` ASC");
$ip_stmt = Database::prepare("SELECT `id_domain`, `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :domainid");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($row['parentdomainid'] == 0) {
// All parentdomains by default have no ssl - ip/port
$parentdomains[$row['id']] = false;
Database::pexecute($ip_stmt, array(
Database::pexecute($ip_stmt, [
'domainid' => $row['id']
));
while ($iprow = $ip_stmt->fetch(\PDO::FETCH_ASSOC)) {
]);
while ($iprow = $ip_stmt->fetch(PDO::FETCH_ASSOC)) {
// If the parentdomain has an ip/port assigned which we know is SSL enabled, set the parentdomain to "true"
if (array_key_exists($iprow['id_ipandports'], $ips)) {
$parentdomains[$row['id']] = true;
@@ -268,8 +276,8 @@ class IntegrityCheck
}
} elseif ($row['ssl_redirect'] == 1) {
// All subdomains with enabled ssl_redirect enabled are stored
if (! isset($subdomains[$row['parentdomainid']])) {
$subdomains[$row['parentdomainid']] = array();
if (!isset($subdomains[$row['parentdomainid']])) {
$subdomains[$row['parentdomainid']] = [];
}
$subdomains[$row['parentdomainid']][] = $row['id'];
}
@@ -278,7 +286,7 @@ class IntegrityCheck
// Check if every parentdomain with enabled ssl_redirect as SSL enabled
foreach ($parentdomains as $id => $sslavailable) {
// This parentdomain has no subdomains
if (! isset($subdomains[$id])) {
if (!isset($subdomains[$id])) {
continue;
}
// This parentdomain has SSL enabled, doesn't matter what status the subdomains have
@@ -289,13 +297,13 @@ class IntegrityCheck
// At this point only parentdomains reside which have ssl_redirect enabled subdomains
if ($fix) {
// We make a blanket update to all subdomains of this parentdomain, doesn't matter which one is wrong, all have to be disabled
Database::pexecute($upd_stmt, array(
Database::pexecute($upd_stmt, [
'domainid' => $id
));
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this");
]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check fixed this");
} else {
// It's just the check, let the function fail
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with ssl_redirect=1 but parent-domain has ssl=0, integrity check can fix this");
return false;
}
}
@@ -311,13 +319,13 @@ class IntegrityCheck
* Check if all subdomain have letsencrypt = 0 if domain has no ssl-port
*
* @param bool $fix
* Fix everything found directly
* Fix everything found directly
*/
public function subdomainLetsencrypt($fix = false)
{
$ips = array();
$parentdomains = array();
$subdomains = array();
$ips = [];
$parentdomains = [];
$subdomains = [];
if ($fix) {
// Prepare update statement for the fixes
@@ -329,7 +337,7 @@ class IntegrityCheck
// Cache all ssl ip/port - combinations
$result_stmt = Database::prepare("SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl` = 1 ORDER BY `id` ASC");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$ips[$row['id']] = $row['ip'] . ':' . $row['port'];
}
@@ -337,14 +345,14 @@ class IntegrityCheck
$result_stmt = Database::prepare("SELECT `id`, `parentdomainid`, `letsencrypt` FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY `id` ASC");
$ip_stmt = Database::prepare("SELECT `id_domain`, `id_ipandports` FROM `" . TABLE_DOMAINTOIP . "` WHERE `id_domain` = :domainid");
Database::pexecute($result_stmt);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($row['parentdomainid'] == 0) {
// All parentdomains by default have no ssl - ip/port
$parentdomains[$row['id']] = false;
Database::pexecute($ip_stmt, array(
Database::pexecute($ip_stmt, [
'domainid' => $row['id']
));
while ($iprow = $ip_stmt->fetch(\PDO::FETCH_ASSOC)) {
]);
while ($iprow = $ip_stmt->fetch(PDO::FETCH_ASSOC)) {
// If the parentdomain has an ip/port assigned which we know is SSL enabled, set the parentdomain to "true"
if (array_key_exists($iprow['id_ipandports'], $ips)) {
$parentdomains[$row['id']] = true;
@@ -352,8 +360,8 @@ class IntegrityCheck
}
} elseif ($row['letsencrypt'] == 1) {
// All subdomains with enabled letsencrypt enabled are stored
if (! isset($subdomains[$row['parentdomainid']])) {
$subdomains[$row['parentdomainid']] = array();
if (!isset($subdomains[$row['parentdomainid']])) {
$subdomains[$row['parentdomainid']] = [];
}
$subdomains[$row['parentdomainid']][] = $row['id'];
}
@@ -362,7 +370,7 @@ class IntegrityCheck
// Check if every parentdomain with enabled letsencrypt as SSL enabled
foreach ($parentdomains as $id => $sslavailable) {
// This parentdomain has no subdomains
if (! isset($subdomains[$id])) {
if (!isset($subdomains[$id])) {
continue;
}
// This parentdomain has SSL enabled, doesn't matter what status the subdomains have
@@ -373,13 +381,13 @@ class IntegrityCheck
// At this point only parentdomains reside which have letsencrypt enabled subdomains
if ($fix) {
// We make a blanket update to all subdomains of this parentdomain, doesn't matter which one is wrong, all have to be disabled
Database::pexecute($upd_stmt, array(
Database::pexecute($upd_stmt, [
'domainid' => $id
));
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this");
]);
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check fixed this");
} else {
// It's just the check, let the function fail
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "found a subdomain with letsencrypt=1 but parent-domain has ssl=0, integrity check can fix this");
return false;
}
}
@@ -396,8 +404,8 @@ class IntegrityCheck
* the customers groups when fcgid / php-fpm is used
*
* @param bool $fix
* fix member/groups
*
* fix member/groups
*
* @return boolean
*/
public function webserverGroupMemberForFcgidPhpFpm($fix = false)
@@ -410,27 +418,27 @@ class IntegrityCheck
$cwg_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_FTP_GROUPS . "` WHERE NOT FIND_IN_SET(:webserveruser, `members`)
");
Database::pexecute($cwg_stmt, array(
Database::pexecute($cwg_stmt, [
'webserveruser' => Settings::Get('system.httpuser')
));
]);
if ($cwg_stmt->rowCount() > 0) {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the webserver-user as group-member, integrity-check can fix that");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the webserver-user as group-member, integrity-check can fix that");
if ($fix) {
// prepare update statement
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_FTP_GROUPS . "` SET `members` = CONCAT(`members`, :additionaluser)
WHERE `id` = :id
");
$upd_data = array(
$upd_data = [
'additionaluser' => "," . Settings::Get('system.httpuser')
);
];
while ($cwg_row = $cwg_stmt->fetch()) {
$upd_data['id'] = $cwg_row['id'];
Database::pexecute($upd_stmt, $upd_data);
}
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the webserver-user as group-member, integrity-check fixed that");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the webserver-user as group-member, integrity-check fixed that");
} else {
return false;
}
@@ -448,8 +456,8 @@ class IntegrityCheck
* fcgid/fpm in froxlor vhost is used
*
* @param bool $fix
* fix member/groups
*
* fix member/groups
*
* @return boolean
*/
public function froxlorLocalGroupMemberForFcgidPhpFpm($fix = false)
@@ -478,27 +486,27 @@ class IntegrityCheck
$cwg_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_FTP_GROUPS . "` WHERE NOT FIND_IN_SET(:localuser, `members`)
");
Database::pexecute($cwg_stmt, array(
Database::pexecute($cwg_stmt, [
'localuser' => $localuser
));
]);
if ($cwg_stmt->rowCount() > 0) {
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the local froxlor-user as group-member, integrity-check can fix that");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers are missing the local froxlor-user as group-member, integrity-check can fix that");
if ($fix) {
// prepare update statement
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_FTP_GROUPS . "` SET `members` = CONCAT(`members`, :additionaluser)
WHERE `id` = :id
");
$upd_data = array(
$upd_data = [
'additionaluser' => "," . $localuser
);
];
while ($cwg_row = $cwg_stmt->fetch()) {
$upd_data['id'] = $cwg_row['id'];
Database::pexecute($upd_stmt, $upd_data);
}
$this->log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the local froxlor-user as group-member, integrity-check fixed that");
$this->log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Customers were missing the local froxlor-user as group-member, integrity-check fixed that");
} else {
return false;
}

View File

@@ -1,37 +1,39 @@
<?php
namespace Froxlor\Database\Manager;
use Froxlor\Database\Database;
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
*
* @since 0.9.31
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can also view it online at
* https://files.froxlor.org/misc/COPYING.txt
*
* @copyright the authors
* @author Froxlor team <team@froxlor.org>
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
*/
namespace Froxlor\Database\Manager;
use Froxlor\Database\Database;
use Froxlor\FroxlorLogger;
use PDO;
/**
* Class DbManagerMySQL
*
* Explicit class for database-management like creating
* and removing databases, users and permissions for MySQL
*
* @copyright (c) the authors
* @author Michael Kaufmann <mkaufmann@nutime.de>
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Classes
*/
class DbManagerMySQL
{
@@ -46,7 +48,7 @@ class DbManagerMySQL
/**
* main constructor
*
* @param \Froxlor\FroxlorLogger $log
* @param FroxlorLogger $log
*/
public function __construct(&$log = null)
{
@@ -71,16 +73,16 @@ class DbManagerMySQL
* @param string $password
* @param string $access_host
* @param bool $p_encrypted
* optional, whether the password is encrypted or not, default false
* optional, whether the password is encrypted or not, default false
* @param bool $update
* optional, whether to update the password only (not create user)
* optional, whether to update the password only (not create user)
*/
public function grantPrivilegesTo($username = null, $password = null, $access_host = null, $p_encrypted = false, $update = false)
{
if (! $update) {
if (!$update) {
// create user
if ($p_encrypted) {
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
$stmt = Database::prepare("
CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED BY PASSWORD :password
");
@@ -94,20 +96,20 @@ class DbManagerMySQL
CREATE USER '" . $username . "'@'" . $access_host . "' IDENTIFIED BY :password
");
}
Database::pexecute($stmt, array(
Database::pexecute($stmt, [
"password" => $password
));
]);
// grant privileges
$stmt = Database::prepare("
GRANT ALL ON `" . $username . "`.* TO :username@:host
");
Database::pexecute($stmt, array(
Database::pexecute($stmt, [
"username" => $username,
"host" => $access_host
));
]);
} else {
// set password
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.6', '<')) {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.6', '<')) {
if ($p_encrypted) {
$stmt = Database::prepare("SET PASSWORD FOR :username@:host = :password");
} else {
@@ -120,11 +122,11 @@ class DbManagerMySQL
$stmt = Database::prepare("ALTER USER :username@:host IDENTIFIED BY :password");
}
}
Database::pexecute($stmt, array(
Database::pexecute($stmt, [
"username" => $username,
"host" => $access_host,
"password" => $password
));
]);
}
}
@@ -136,29 +138,29 @@ class DbManagerMySQL
*/
public function deleteDatabase($dbname = null)
{
if (Database::getAttribute(\PDO::ATTR_SERVER_VERSION) < '5.0.2') {
if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') {
// failsafe if user has been deleted manually (requires MySQL 4.1.2+)
$stmt = Database::prepare("REVOKE ALL PRIVILEGES, GRANT OPTION FROM `" . $dbname . "`");
Database::pexecute($stmt, array(), false);
Database::pexecute($stmt, [], false);
}
$host_res_stmt = Database::prepare("
SELECT `Host` FROM `mysql`.`user` WHERE `User` = :dbname");
Database::pexecute($host_res_stmt, array(
Database::pexecute($host_res_stmt, [
'dbname' => $dbname
));
]);
// as of MySQL 5.0.2 this also revokes privileges. (requires MySQL 4.1.2+)
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
$drop_stmt = Database::prepare("DROP USER :dbname@:host");
} else {
$drop_stmt = Database::prepare("DROP USER IF EXISTS :dbname@:host");
}
while ($host = $host_res_stmt->fetch(\PDO::FETCH_ASSOC)) {
Database::pexecute($drop_stmt, array(
while ($host = $host_res_stmt->fetch(PDO::FETCH_ASSOC)) {
Database::pexecute($drop_stmt, [
'dbname' => $dbname,
'host' => $host['Host']
), false);
], false);
}
$drop_stmt = Database::prepare("DROP DATABASE IF EXISTS `" . $dbname . "`");
@@ -173,21 +175,21 @@ class DbManagerMySQL
*/
public function deleteUser($username = null, $host = null)
{
if (Database::getAttribute(\PDO::ATTR_SERVER_VERSION) < '5.0.2') {
if (Database::getAttribute(PDO::ATTR_SERVER_VERSION) < '5.0.2') {
// Revoke privileges (only required for MySQL 4.1.2 - 5.0.1)
$stmt = Database::prepare("REVOKE ALL PRIVILEGES ON * . * FROM `" . $username . "`@`" . $host . "`");
Database::pexecute($stmt);
}
// as of MySQL 5.0.2 this also revokes privileges. (requires MySQL 4.1.2+)
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
if (version_compare(Database::getAttribute(PDO::ATTR_SERVER_VERSION), '5.7.0', '<')) {
$stmt = Database::prepare("DROP USER :username@:host");
} else {
$stmt = Database::prepare("DROP USER IF EXISTS :username@:host");
}
Database::pexecute($stmt, array(
Database::pexecute($stmt, [
"username" => $username,
"host" => $host
));
]);
}
/**
@@ -195,12 +197,12 @@ class DbManagerMySQL
*
* @param string $username
* @param string $host
* (unused in mysql)
* (unused in mysql)
*/
public function disableUser($username = null, $host = null)
{
$stmt = Database::prepare('REVOKE ALL PRIVILEGES, GRANT OPTION FROM `' . $username . '`@`' . $host . '`');
Database::pexecute($stmt, array(), false);
Database::pexecute($stmt, [], false);
}
/**
@@ -232,8 +234,8 @@ class DbManagerMySQL
* return an array of all usernames used in that DBMS
*
* @param bool $user_only
* if false, * will be selected from mysql.user and slightly different array will be generated
*
* if false, * will be selected from mysql.user and slightly different array will be generated
*
* @return array
*/
public function getAllSqlUsers($user_only = true)
@@ -244,14 +246,14 @@ class DbManagerMySQL
$result_stmt = Database::prepare('SELECT `User` FROM mysql.user');
}
Database::pexecute($result_stmt);
$allsqlusers = array();
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
$allsqlusers = [];
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($user_only == false) {
if (! isset($allsqlusers[$row['User']]) || ! is_array($allsqlusers[$row['User']])) {
$allsqlusers[$row['User']] = array(
if (!isset($allsqlusers[$row['User']]) || !is_array($allsqlusers[$row['User']])) {
$allsqlusers[$row['User']] = [
'password' => $row['Password'] ?? $row['authentication_string'],
'hosts' => array()
);
'hosts' => []
];
}
$allsqlusers[$row['User']]['hosts'][] = $row['Host'];
} else {