Files
Froxlor/lib/Froxlor/Ajax/Ajax.php
Michael Kaufmann 5ba28ef599 fix wrong request-parameter reading for table-column mangement
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-02-17 19:32:27 +01:00

387 lines
11 KiB
PHP

<?php
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* 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.
*
* 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\Ajax;
use Exception;
use DateTime;
use Froxlor\Config\ConfigDisplay;
use Froxlor\Config\ConfigParser;
use Froxlor\CurrentUser;
use Froxlor\Database\Database;
use Froxlor\FileDir;
use Froxlor\Froxlor;
use Froxlor\Http\HttpClient;
use Froxlor\Install\Update;
use Froxlor\Settings;
use Froxlor\UI\Listing;
use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response;
use Froxlor\Validate\Validate;
class Ajax
{
protected string $action;
protected string $theme;
protected array $userinfo;
/**
* @throws Exception
*/
public function __construct()
{
$this->action = Request::any('action');
$this->theme = Request::any('theme', 'Froxlor');
UI::sendHeaders();
UI::sendSslHeaders();
}
/**
* @throws Exception
*/
public function handle()
{
$this->userinfo = $this->getValidatedSession();
switch ($this->action) {
case 'newsfeed':
return $this->getNewsfeed();
case 'updatecheck':
return $this->getUpdateCheck();
case 'searchglobal':
return $this->searchGlobal();
case 'updatetablelisting':
return $this->updateTablelisting();
case 'resettablelisting':
return $this->resetTablelisting();
case 'editapikey':
return $this->editApiKey();
case 'getConfigDetails':
return $this->getConfigDetails();
case 'getConfigJsonExport':
return $this->getConfigJsonExport();
case 'loadLanguageString':
return $this->loadLanguageString();
default:
return $this->errorResponse('Action not found!');
}
}
/**
* @throws Exception
*/
private function getValidatedSession(): array
{
if (CurrentUser::hasSession() == false) {
throw new Exception("No valid session");
}
return CurrentUser::getData();
}
/**
* @throws Exception
*/
private function getNewsfeed()
{
UI::initTwig();
$feed = "https://inside.froxlor.org/news/";
// Set custom feed if provided
$role = Request::get('role');
if ($role == "customer") {
$custom_feed = Settings::Get("customer.news_feed_url");
if (!empty(trim($custom_feed))) {
$feed = $custom_feed;
}
}
// Check for simplexml_load_file
if (!function_exists("simplexml_load_file")) {
return $this->errorResponse([
"Newsfeed not available due to missing php-simplexml extension",
"Please install the php-simplexml extension in order to view our newsfeed."
]);
}
// Check for curl_version
if (!function_exists('curl_version')) {
return $this->errorResponse([
"Newsfeed not available due to missing php-curl extension",
"Please install the php-curl extension in order to view our newsfeed."
]);
}
$output = HttpClient::urlGet($feed);
$news = simplexml_load_string(trim($output));
if ($news === false) {
$err = [];
foreach (libxml_get_errors() as $error) {
$err[] = $error->message;
}
return $this->errorResponse(
$err
);
}
// Handle items
if ($news) {
$items = null;
for ($i = 0; $i < 3; $i++) {
$item = $news->channel->item[$i];
$title = (string)$item->title;
$link = (string)$item->link;
$date = date("d.m.Y", strtotime($item->pubDate));
$content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description));
$content = substr($content, 0, 150) . "...";
$items .= UI::twig()->render(UI::validateThemeTemplate('/user/newsfeeditem.html.twig', $this->theme), [
'link' => $link,
'title' => $title,
'date' => $date,
'content' => $content
]);
}
return $this->jsonResponse($items);
} else {
return $this->errorResponse('No Newsfeeds available at the moment.');
}
}
public function errorResponse($message, int $response_code = 500)
{
header("Content-Type: application/json");
return \Froxlor\Api\Response::jsonErrorResponse($message, $response_code);
}
public function jsonResponse($value, int $response_code = 200)
{
header("Content-Type: application/json");
return \Froxlor\Api\Response::jsonResponse($value, $response_code);
}
private function getUpdateCheck()
{
UI::initTwig();
try {
$json_result = \Froxlor\Api\Commands\Froxlor::getLocal($this->userinfo)->checkUpdate();
$result = json_decode($json_result, true)['data'];
$result['full_version'] = Froxlor::getFullVersion();
$result['dbversion'] = Froxlor::DBVERSION;
$uc_data = Update::getUpdateCheckData();
$result['last_update_check'] = $uc_data['ts'];
$result['channel'] = Settings::Get('system.update_channel');
$result_rendered = UI::twig()->render(UI::validateThemeTemplate('/misc/version_top.html.twig', $this->theme), $result);
return $this->jsonResponse($result_rendered);
} catch (Exception $e) {
// don't display anything if just not allowed due to permissions
if ($e->getCode() != 403) {
return $this->errorResponse($e->getMessage(), $e->getCode());
}
}
}
/**
* search globally in various resources
*/
private function searchGlobal()
{
$searchtext = Request::any('searchtext');
$result = [];
// settings
$result_settings = [];
if (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {
$result_settings = GlobalSearch::searchSettings($searchtext, $this->userinfo);
}
// all searchable entities
$result_entities = GlobalSearch::searchGlobal($searchtext, $this->userinfo);
$result = array_merge($result_settings, $result_entities);
return $this->jsonResponse($result);
}
private function updateTablelisting()
{
$columns = [];
foreach ((Request::post('columns') ?? []) as $value) {
$columns[] = $value;
}
if (!empty($columns)) {
$columns = Listing::storeColumnListingForUser([Request::get('listing') => $columns]);
return $this->jsonResponse($columns);
}
return $this->errorResponse('At least one column must be selected', 406);
}
private function resetTablelisting()
{
Listing::deleteColumnListingForUser([Request::get('listing') => []]);
return $this->jsonResponse([]);
}
private function editApiKey()
{
$keyid = Request::post('id', 0);
$allowed_from = Request::post('allowed_from', "");
$valid_until = Request::post('valid_until', "");
if (empty($keyid)) {
return $this->errorResponse('Invalid call', 406);
}
// validate allowed_from
if (!empty($allowed_from)) {
$ip_list = array_map('trim', explode(",", $allowed_from));
$_check_list = $ip_list;
foreach ($_check_list as $idx => $ip) {
if (Validate::validate_ip2($ip, true, 'invalidip', true, true, true) == false) {
return $this->errorResponse('Invalid ip address', 406);
}
// check for cidr
if (strpos($ip, '/') !== false) {
$ipparts = explode("/", $ip);
// shorten IP
$ip = inet_ntop(inet_pton($ipparts[0]));
// re-add cidr
$ip .= '/' . $ipparts[1];
} else {
// shorten IP
$ip = inet_ntop(inet_pton($ip));
}
$ip_list[$idx] = $ip;
}
$allowed_from = implode(",", array_unique($ip_list));
}
if (!empty($valid_until)) {
$valid_until_db = DateTime::createFromFormat('Y-m-d\TH:i', $valid_until)->format('U');
} else {
$valid_until_db = -1;
}
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_API_KEYS . "` SET
`valid_until` = :vu, `allowed_from` = :af
WHERE `id` = :keyid AND `adminid` = :aid AND `customerid` = :cid
");
if ((int)$this->userinfo['adminsession'] == 1) {
$cid = 0;
} else {
$cid = $this->userinfo['customerid'];
}
Database::pexecute($upd_stmt, [
'keyid' => $keyid,
'af' => $allowed_from,
'vu' => $valid_until_db,
'aid' => $this->userinfo['adminid'],
'cid' => $cid
]);
return $this->jsonResponse(['allowed_from' => $allowed_from, 'valid_until' => $valid_until]);
}
/**
* return parsed commands/files of configuration templates
*/
private function getConfigDetails()
{
if (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {
$distribution = Request::post('distro', "");
$section = Request::post('section', "");
$daemon = Request::post('daemon', "");
// validate distribution config-xml exists
$config_dir = FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/lib/configfiles/');
if (!file_exists($config_dir . "/" . $distribution . ".xml")) {
return $this->errorResponse("Unknown distribution. The configuration could not be found.");
}
// read in all configurations
$configfiles = new ConfigParser($config_dir . "/" . $distribution . ".xml");
// get the services
$services = $configfiles->getServices();
// validate selected service exists for this distribution
if (!isset($services[$section])) {
return $this->errorResponse("Unknown category for selected distribution");
}
// get the daemons
$daemons = $services[$section]->getDaemons();
// validate selected daemon exists for this section
if (!isset($daemons[$daemon])) {
return $this->errorResponse("Unknown service for selected category");
}
// finally the config-steps
$confarr = $daemons[$daemon]->getConfig();
// get parsed content
UI::initTwig();
$content = ConfigDisplay::fromConfigArr($confarr, $configfiles->distributionEditor, $this->theme);
return $this->jsonResponse([
'title' => $configfiles->getCompleteDistroName() . '&nbsp;&raquo;&nbsp' . $services[$section]->title . '&nbsp;&raquo;&nbsp' . $daemons[$daemon]->title,
'content' => $content
]);
}
return $this->errorResponse('Not allowed', 403);
}
/**
* download JSON export of config-selection
*/
private function getConfigJsonExport()
{
if (isset($this->userinfo['adminsession']) && $this->userinfo['adminsession'] == 1 && $this->userinfo['change_serversettings'] == 1) {
$params = $_GET;
unset($params['action']);
unset($params['finish']);
unset($params['csrf_token']);
header('Content-disposition: attachment; filename=froxlor-config-' . time() . '.json');
return $this->jsonResponse($params);
}
return $this->errorResponse('Not allowed', 403);
}
/**
* loads a given language string by its identifier
*/
private function loadLanguageString()
{
$langid = Request::post('langid', "");
if (preg_match('/^([a-zA-Z\.]+)$/', $langid)) {
return $this->jsonResponse(lng($langid));
}
return $this->errorResponse('Invalid identifier: ' . $langid, 406);
}
}