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,33 +1,61 @@
<?php
namespace Froxlor\Api\Commands;
use Froxlor\Database\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 Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package API
* @since 0.10.0
*
* 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
*/
class Froxlor extends \Froxlor\Api\ApiCommand
namespace Froxlor\Api\Commands;
use Exception;
use Froxlor\Api\ApiCommand;
use Froxlor\Cron\TaskId;
use Froxlor\Database\Database;
use Froxlor\Database\IntegrityCheck;
use Froxlor\FroxlorLogger;
use Froxlor\Http\HttpClient;
use Froxlor\Settings;
use Froxlor\SImExporter;
use Froxlor\System\Cronjob;
use Froxlor\System\Crypt;
use Froxlor\UI\Response;
use PDO;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
/**
* @since 0.10.0
*/
class Froxlor extends ApiCommand
{
/**
* checks whether there is a newer version of froxlor available
*
* @access admin
* @throws \Exception
* @return string json-encoded array
* @throws Exception
*/
public function checkUpdate()
{
@@ -36,12 +64,12 @@ class Froxlor extends \Froxlor\Api\ApiCommand
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
if (function_exists('curl_version')) {
// log our actions
$this->logger()->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates");
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates");
// check for new version
try {
$latestversion = \Froxlor\Http\HttpClient::urlGet(UPDATE_URI, true, 3);
} catch (\Exception $e) {
$latestversion = HttpClient::urlGet(UPDATE_URI, true, 3);
} catch (Exception $e) {
$latestversion = \Froxlor\Froxlor::getVersion() . "|Version-check currently unavailable, please try again later";
}
$latestversion = explode('|', $latestversion);
@@ -58,11 +86,11 @@ class Froxlor extends \Froxlor\Api\ApiCommand
$message_addinfo = $_message;
// not numeric -> error-message
if (! preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) {
if (!preg_match('/^((\d+\\.)(\d+\\.)(\d+\\.)?(\d+)?(\-(svn|dev|rc)(\d+))?)$/', $_version)) {
// check for customized version to not output
// "There is a newer version of froxlor" besides the error-message
$isnewerversion = - 1;
} elseif (\Froxlor\Froxlor::versionCompare2($this->version, $_version) == - 1) {
$isnewerversion = -1;
} elseif (\Froxlor\Froxlor::versionCompare2($this->version, $_version) == -1) {
// there is a newer version - yay
$isnewerversion = 1;
} else {
@@ -74,92 +102,92 @@ class Froxlor extends \Froxlor\Api\ApiCommand
// zum update schritt #1 -> download
if ($isnewerversion == 1) {
$text = 'There is a newer version available: "' . $_version . '" (Your current version is: ' . $this->version . ')';
return $this->response(array(
return $this->response([
'isnewerversion' => $isnewerversion,
'version' => $_version,
'message' => $text,
'link' => $version_link,
'additional_info' => $message_addinfo
));
]);
} elseif ($isnewerversion == 0) {
// all good
return $this->response(array(
return $this->response([
'isnewerversion' => $isnewerversion,
'version' => $version_label,
'message' => "",
'link' => $version_link,
'additional_info' => $message_addinfo
));
]);
} else {
\Froxlor\UI\Response::standard_error('customized_version', '', true);
Response::standardError('customized_version', '', true);
}
}
}
return $this->response(array(
return $this->response([
'isnewerversion' => 0,
'version' => $this->version . $this->branding,
'message' => 'Version-check not available due to missing php-curl extension',
'link' => UPDATE_URI . '/pretty',
'additional_info' => ""
), 502);
], 502);
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* import settings
*
* @param string $json_str
* content of exported froxlor-settings json file
*
* content of exported froxlor-settings json file
*
* @access admin
* @throws \Exception
* @return string json-encoded bool
* @throws Exception
*/
public function importSettings()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$json_str = $this->getParam('json_str');
$this->logger()->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings");
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings");
try {
\Froxlor\SImExporter::import($json_str);
\Froxlor\System\Cronjob::inserttask(\Froxlor\Cron\TaskId::REBUILD_VHOST);
\Froxlor\System\Cronjob::inserttask(\Froxlor\Cron\TaskId::CREATE_QUOTA);
SImExporter::import($json_str);
Cronjob::inserttask(TaskId::REBUILD_VHOST);
Cronjob::inserttask(TaskId::CREATE_QUOTA);
// Using nameserver, insert a task which rebuilds the server config
\Froxlor\System\Cronjob::inserttask(\Froxlor\Cron\TaskId::REBUILD_DNS);
Cronjob::inserttask(TaskId::REBUILD_DNS);
// cron.d file
\Froxlor\System\Cronjob::inserttask(\Froxlor\Cron\TaskId::REBUILD_CRON);
Cronjob::inserttask(TaskId::REBUILD_CRON);
return $this->response(true);
} catch (\Exception $e) {
throw new \Exception($e->getMessage(), 406);
} catch (Exception $e) {
throw new Exception($e->getMessage(), 406);
}
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* export settings
*
* @access admin
* @throws \Exception
* @return string json-string
* @throws Exception
*/
public function exportSettings()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$this->logger()->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " exported settings");
$json_export = \Froxlor\SImExporter::export();
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " exported settings");
$json_export = SImExporter::export();
return $this->response($json_export);
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* return a list of all settings
*
* @access admin
* @throws \Exception
* @return string json-encoded array count|list
* @throws Exception
*/
public function listSettings()
{
@@ -168,30 +196,30 @@ class Froxlor extends \Froxlor\Api\ApiCommand
SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` ORDER BY settinggroup ASC, varname ASC
");
Database::pexecute($sel_stmt, null, true, true);
$result = array();
while ($row = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
$result[] = array(
$result = [];
while ($row = $sel_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = [
'key' => $row['settinggroup'] . '.' . $row['varname'],
'value' => $row['value']
);
];
}
return $this->response(array(
return $this->response([
'count' => count($result),
'list' => $result
));
]);
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* return a setting by settinggroup.varname couple
*
* @param string $key
* settinggroup.varname couple
*
* settinggroup.varname couple
*
* @access admin
* @throws \Exception
* @return string
* @throws Exception
*/
public function getSetting()
{
@@ -199,37 +227,37 @@ class Froxlor extends \Froxlor\Api\ApiCommand
$setting = $this->getParam('key');
return $this->response(Settings::Get($setting));
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* updates a setting
*
* @param string $key
* settinggroup.varname couple
* settinggroup.varname couple
* @param string $value
* optional the new value, default is ''
*
* optional the new value, default is ''
*
* @access admin
* @throws \Exception
* @return string
* @throws Exception
*/
public function updateSetting()
{
// currently not implemented as it requires validation too so no wrong settings are being stored via API
throw new \Exception("Not available yet.", 501);
throw new Exception("Not available yet.", 501);
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$setting = $this->getParam('key');
$value = $this->getParam('value', true, '');
$oldvalue = Settings::Get($setting);
if (is_null($oldvalue)) {
throw new \Exception("Setting '" . $setting . "' could not be found");
throw new Exception("Setting '" . $setting . "' could not be found");
}
$this->logger()->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] Changing setting '" . $setting . "' from '" . $oldvalue . "' to '" . $value . "'");
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] Changing setting '" . $setting . "' from '" . $oldvalue . "' to '" . $value . "'");
return $this->response(Settings::Set($setting, $value, true));
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
@@ -240,56 +268,56 @@ class Froxlor extends \Froxlor\Api\ApiCommand
*/
public function generatePassword()
{
return $this->response(\Froxlor\System\Crypt::generatePassword());
return $this->response(Crypt::generatePassword());
}
/**
* can be used to remotely run the integritiy checks froxlor implements
*
* @access admin
* @throws \Exception
* @return string
* @throws Exception
*/
public function integrityCheck()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$integrity = new \Froxlor\Database\IntegrityCheck();
$integrity = new IntegrityCheck();
$result = $integrity->checkAll();
if ($result) {
return $this->response(null, 204);
}
throw new \Exception("Some checks failed.", 406);
throw new Exception("Some checks failed.", 406);
}
throw new \Exception("Not allowed to execute given command.", 403);
throw new Exception("Not allowed to execute given command.", 403);
}
/**
* returns a list of all available api functions
*
* @param string $module
* optional, return list of functions for a specific module
*
* optional, return list of functions for a specific module
*
* @access admin, customer
* @throws \Exception
* @return string json-encoded array
* @throws Exception
*/
public function listFunctions()
{
$module = $this->getParam('module', true, '');
$functions = array();
$functions = [];
if ($module != null) {
// check existence
$this->requireModules($module);
// now get all static functions
$reflection = new \ReflectionClass(__NAMESPACE__ . '\\' . $module);
$_functions = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
$reflection = new ReflectionClass(__NAMESPACE__ . '\\' . $module);
$_functions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($_functions as $func) {
if ($func->class == __NAMESPACE__ . '\\' . $module && $func->isPublic()) {
array_push($functions, array_merge(array(
array_push($functions, array_merge([
'module' => $module,
'function' => $func->name
), $this->getParamListFromDoc($module, $func->name)));
], $this->getParamListFromDoc($module, $func->name)));
}
}
} else {
@@ -298,37 +326,37 @@ class Froxlor extends \Froxlor\Api\ApiCommand
// valid directory?
if (is_dir($path)) {
// create RecursiveIteratorIterator
$its = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
$its = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
// check every file
foreach ($its as $it) {
// does it match the Filename pattern?
$matches = array();
$matches = [];
if (preg_match("/^(.+)\.php$/i", $it->getFilename(), $matches)) {
// check for existence
try {
// set the module to be in our namespace
$mod = $matches[1];
$this->requireModules($mod);
} catch (\Exception $e) {
} catch (Exception $e) {
// @todo log?
continue;
}
// now get all static functions
$reflection = new \ReflectionClass(__NAMESPACE__ . '\\' . $mod);
$_functions = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
$reflection = new ReflectionClass(__NAMESPACE__ . '\\' . $mod);
$_functions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($_functions as $func) {
if ($func->class == __NAMESPACE__ . '\\' . $mod && $func->isPublic() && ! $func->isStatic()) {
array_push($functions, array_merge(array(
if ($func->class == __NAMESPACE__ . '\\' . $mod && $func->isPublic() && !$func->isStatic()) {
array_push($functions, array_merge([
'module' => $matches[1],
'function' => $func->name
), $this->getParamListFromDoc($matches[1], $func->name)));
], $this->getParamListFromDoc($matches[1], $func->name)));
}
}
}
}
} else {
// yikes - no valid directory to check
throw new \Exception("Cannot search directory '" . $path . "'. No such directory.", 500);
throw new Exception("Cannot search directory '" . $path . "'. No such directory.", 500);
}
}
@@ -336,6 +364,45 @@ class Froxlor extends \Froxlor\Api\ApiCommand
return $this->response($functions);
}
/**
* this functions is used to check the availability
* of a given list of modules.
* If either one of
* them are not found, throw an Exception
*
* @param string|array $modules
*
* @throws Exception
*/
private function requireModules($modules = null)
{
if ($modules != null) {
// no array -> create one
if (!is_array($modules)) {
$modules = [
$modules
];
}
// check all the modules
foreach ($modules as $module) {
try {
$module = __NAMESPACE__ . '\\' . $module;
// can we use the class?
if (class_exists($module)) {
continue;
} else {
throw new Exception('The required class "' . $module . '" could not be found but the module-file exists', 404);
}
} catch (Exception $e) {
// The autoloader will throw an Exception
// that the required class could not be found
// but we want a nicer error-message for this here
throw new Exception('The required module "' . $module . '" could not be found', 404);
}
}
}
}
/**
* generate an api-response to list all parameters and the return-value of
* a given module.function-combination
@@ -343,59 +410,59 @@ class Froxlor extends \Froxlor\Api\ApiCommand
* @param string $module
* @param string $function
*
* @throws \Exception
* @return array|bool
* @throws Exception
*/
private function getParamListFromDoc($module = null, $function = null)
{
try {
// set the module
$cls = new \ReflectionMethod(__NAMESPACE__ . '\\' . $module, $function);
$cls = new ReflectionMethod(__NAMESPACE__ . '\\' . $module, $function);
$comment = $cls->getDocComment();
if ($comment == false) {
return array(
return [
'head' => 'There is no comment-block for "' . $module . '.' . $function . '"'
);
];
}
$clines = explode("\n", $comment);
$result = array();
$result['params'] = array();
$result = [];
$result['params'] = [];
$param_desc = false;
$r = array();
$r = [];
foreach ($clines as $c) {
$c = trim($c);
// check param-section
if (strpos($c, '@param')) {
preg_match('/^\*\s\@param\s(.+)\s(\$\w+)(\s.*)?/', $c, $r);
// cut $ off the parameter-name as it is not wanted in the api-request
$result['params'][] = array(
$result['params'][] = [
'parameter' => substr($r[2], 1),
'type' => $r[1],
'desc' => (isset($r[3]) ? trim($r['3']) : '')
);
];
$param_desc = true;
} elseif (strpos($c, '@access')) {
// check access-section
preg_match('/^\*\s\@access\s(.*)/', $c, $r);
if (! isset($r[0]) || empty($r[0])) {
if (!isset($r[0]) || empty($r[0])) {
$r[1] = 'This function has no restrictions';
}
$result['access'] = array(
$result['access'] = [
'groups' => (isset($r[1]) ? trim($r[1]) : '')
);
];
} elseif (strpos($c, '@return')) {
// check return-section
preg_match('/^\*\s\@return\s(\w+)(\s.*)?/', $c, $r);
if (! isset($r[0]) || empty($r[0])) {
if (!isset($r[0]) || empty($r[0])) {
$r[1] = 'null';
$r[2] = 'This function has no return value given';
}
$result['return'] = array(
$result['return'] = [
'type' => $r[1],
'desc' => (isset($r[2]) ? trim($r[2]) : '')
);
} elseif (! empty($c) && strpos($c, '@throws') === false) {
];
} elseif (!empty($c) && strpos($c, '@throws') === false) {
// check throws-section
if (substr($c, 0, 3) == "/**") {
continue;
@@ -411,7 +478,7 @@ class Froxlor extends \Froxlor\Api\ApiCommand
if ($param_desc) {
$result['params'][count($result['params']) - 1]['desc'] .= $c;
} else {
if (! isset($result['head']) || empty($result['head'])) {
if (!isset($result['head']) || empty($result['head'])) {
$result['head'] = $c . " ";
} else {
$result['head'] .= $c . " ";
@@ -422,47 +489,8 @@ class Froxlor extends \Froxlor\Api\ApiCommand
}
$result['head'] = trim($result['head']);
return $result;
} catch (\ReflectionException $e) {
return array();
}
}
/**
* this functions is used to check the availability
* of a given list of modules.
* If either one of
* them are not found, throw an Exception
*
* @param string|array $modules
*
* @throws \Exception
*/
private function requireModules($modules = null)
{
if ($modules != null) {
// no array -> create one
if (! is_array($modules)) {
$modules = array(
$modules
);
}
// check all the modules
foreach ($modules as $module) {
try {
$module = __NAMESPACE__ . '\\' . $module;
// can we use the class?
if (class_exists($module)) {
continue;
} else {
throw new \Exception('The required class "' . $module . '" could not be found but the module-file exists', 404);
}
} catch (\Exception $e) {
// The autoloader will throw an Exception
// that the required class could not be found
// but we want a nicer error-message for this here
throw new \Exception('The required module "' . $module . '" could not be found', 404);
}
}
} catch (ReflectionException $e) {
return [];
}
}
}