major refactoring of almost all files
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user