Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5bece64ce | ||
|
|
0034681412 | ||
|
|
bd5b99dc1c | ||
|
|
2feb802094 | ||
|
|
7b08a71c59 | ||
|
|
2a84e9c120 | ||
|
|
d854e8e991 | ||
|
|
0a363910d6 | ||
|
|
b23d5cd909 | ||
|
|
3b753aa69d | ||
|
|
492cd288bc | ||
|
|
47938c5082 | ||
|
|
d090e48544 | ||
|
|
314e4407a0 | ||
|
|
dff7530cc5 | ||
|
|
19423c9644 |
@@ -57,7 +57,7 @@ May be found in [COPYING](COPYING)
|
|||||||
### Tarball
|
### Tarball
|
||||||
https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1)
|
https://files.froxlor.org/releases/froxlor-latest.tar.gz [MD5](https://files.froxlor.org/releases/froxlor-latest.tar.gz.md5) [SHA1](https://files.froxlor.org/releases/froxlor-latest.tar.gz.sha1)
|
||||||
|
|
||||||
### Debian / Ubutnu repository
|
### Debian / Ubuntu repository
|
||||||
|
|
||||||
[HowTo](https://docs.froxlor.org/latest/general/installation/apt-package.html)
|
[HowTo](https://docs.froxlor.org/latest/general/installation/apt-package.html)
|
||||||
|
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ if ($action == '') {
|
|||||||
if (isset($_POST['prepare']) && $_POST['prepare'] == 'prepare') {
|
if (isset($_POST['prepare']) && $_POST['prepare'] == 'prepare') {
|
||||||
// email templates
|
// email templates
|
||||||
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
||||||
|
if (!array_key_exists($language, $languages)) {
|
||||||
|
Response::standardError('templatelanguageinvalid');
|
||||||
|
}
|
||||||
$template = Validate::validate($_POST['template'], 'template');
|
$template = Validate::validate($_POST['template'], 'template');
|
||||||
|
|
||||||
$result_stmt = Database::prepare("
|
$result_stmt = Database::prepare("
|
||||||
@@ -288,6 +291,9 @@ if ($action == '') {
|
|||||||
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && !isset($_POST['filesend'])) {
|
} elseif (isset($_POST['send']) && $_POST['send'] == 'send' && !isset($_POST['filesend'])) {
|
||||||
// email templates
|
// email templates
|
||||||
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
$language = htmlentities(Validate::validate($_POST['language'], 'language', '/^[^\r\n\0"\']+$/', 'nolanguageselect'));
|
||||||
|
if (!array_key_exists($language, $languages)) {
|
||||||
|
Response::standardError('templatelanguageinvalid');
|
||||||
|
}
|
||||||
$template = Validate::validate($_POST['template'], 'template');
|
$template = Validate::validate($_POST['template'], 'template');
|
||||||
$subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate');
|
$subject = Validate::validate($_POST['subject'], 'subject', '/^[^\r\n\0]+$/', 'nosubjectcreate');
|
||||||
$mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate');
|
$mailbody = Validate::validate($_POST['mailbody'], 'mailbody', '/^[^\0]+$/', 'nomailbodycreate');
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use Froxlor\Froxlor;
|
|||||||
use Froxlor\UI\Panel\UI;
|
use Froxlor\UI\Panel\UI;
|
||||||
use Froxlor\UI\Request;
|
use Froxlor\UI\Request;
|
||||||
use Froxlor\UI\Response;
|
use Froxlor\UI\Response;
|
||||||
|
use Froxlor\Database\Database;
|
||||||
|
|
||||||
// This file is being included in admin_domains and customer_domains
|
// This file is being included in admin_domains and customer_domains
|
||||||
// and therefore does not need to require lib/init.php
|
// and therefore does not need to require lib/init.php
|
||||||
|
|||||||
@@ -697,7 +697,7 @@ opcache.validate_timestamps'),
|
|||||||
('system', 'distribution', ''),
|
('system', 'distribution', ''),
|
||||||
('system', 'update_channel', 'stable'),
|
('system', 'update_channel', 'stable'),
|
||||||
('system', 'updatecheck_data', ''),
|
('system', 'updatecheck_data', ''),
|
||||||
('system', 'update_notify_last', '2.0.9'),
|
('system', 'update_notify_last', '2.0.10'),
|
||||||
('system', 'traffictool', 'goaccess'),
|
('system', 'traffictool', 'goaccess'),
|
||||||
('api', 'enabled', '0'),
|
('api', 'enabled', '0'),
|
||||||
('2fa', 'enabled', '1'),
|
('2fa', 'enabled', '1'),
|
||||||
@@ -741,7 +741,7 @@ opcache.validate_timestamps'),
|
|||||||
('panel', 'logo_overridetheme', '0'),
|
('panel', 'logo_overridetheme', '0'),
|
||||||
('panel', 'logo_overridecustom', '0'),
|
('panel', 'logo_overridecustom', '0'),
|
||||||
('panel', 'settings_mode', '0'),
|
('panel', 'settings_mode', '0'),
|
||||||
('panel', 'version', '2.0.9'),
|
('panel', 'version', '2.0.10'),
|
||||||
('panel', 'db_version', '202301180');
|
('panel', 'db_version', '202301180');
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -377,3 +377,8 @@ if (Froxlor::isFroxlorVersion('2.0.8')) {
|
|||||||
Update::showUpdateStep("Updating from 2.0.8 to 2.0.9", false);
|
Update::showUpdateStep("Updating from 2.0.8 to 2.0.9", false);
|
||||||
Froxlor::updateToVersion('2.0.9');
|
Froxlor::updateToVersion('2.0.9');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.0.9')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.0.9 to 2.0.10", false);
|
||||||
|
Froxlor::updateToVersion('2.0.10');
|
||||||
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class Ajax
|
|||||||
$content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description));
|
$content = preg_replace("/[\r\n]+/", " ", strip_tags($item->description));
|
||||||
$content = substr($content, 0, 150) . "...";
|
$content = substr($content, 0, 150) . "...";
|
||||||
|
|
||||||
$items .= UI::twig()->render($this->theme . '/user/newsfeeditem.html.twig', [
|
$items .= UI::twig()->render(UI::validateThemeTemplate('/user/newsfeeditem.html.twig', $this->theme), [
|
||||||
'link' => $link,
|
'link' => $link,
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'date' => $date,
|
'date' => $date,
|
||||||
@@ -201,7 +201,7 @@ class Ajax
|
|||||||
$result['last_update_check'] = $uc_data['ts'];
|
$result['last_update_check'] = $uc_data['ts'];
|
||||||
$result['channel'] = Settings::Get('system.update_channel');
|
$result['channel'] = Settings::Get('system.update_channel');
|
||||||
|
|
||||||
$result_rendered = UI::twig()->render($this->theme . '/misc/version_top.html.twig', $result);
|
$result_rendered = UI::twig()->render(UI::validateThemeTemplate('/misc/version_top.html.twig', $this->theme), $result);
|
||||||
return $this->jsonResponse($result_rendered);
|
return $this->jsonResponse($result_rendered);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// don't display anything if just not allowed due to permissions
|
// don't display anything if just not allowed due to permissions
|
||||||
|
|||||||
@@ -117,6 +117,6 @@ class Api
|
|||||||
|
|
||||||
private function stripcslashesDeep($value)
|
private function stripcslashesDeep($value)
|
||||||
{
|
{
|
||||||
return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : stripcslashes($value);
|
return is_array($value) ? array_map([$this, 'stripcslashesDeep'], $value) : (!empty($value) ? stripcslashes($value) : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use Froxlor\Cron\TaskId;
|
|||||||
use Froxlor\Database\Database;
|
use Froxlor\Database\Database;
|
||||||
use Froxlor\FroxlorLogger;
|
use Froxlor\FroxlorLogger;
|
||||||
use Froxlor\System\Cronjob;
|
use Froxlor\System\Cronjob;
|
||||||
|
use Froxlor\UI\Response;
|
||||||
use Froxlor\Validate\Validate;
|
use Froxlor\Validate\Validate;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
@@ -41,6 +42,14 @@ use PDO;
|
|||||||
class Cronjobs extends ApiCommand implements ResourceEntity
|
class Cronjobs extends ApiCommand implements ResourceEntity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private array $allowed_intervals = [
|
||||||
|
'MINUTE',
|
||||||
|
'HOUR',
|
||||||
|
'DAY',
|
||||||
|
'WEEK',
|
||||||
|
'MONTH'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You cannot add new cronjobs yet.
|
* You cannot add new cronjobs yet.
|
||||||
*/
|
*/
|
||||||
@@ -118,6 +127,10 @@ class Cronjobs extends ApiCommand implements ResourceEntity
|
|||||||
$interval_value = Validate::validate($interval_value, 'interval_value', '/^([0-9]+)$/Di', 'stringisempty', [], true);
|
$interval_value = Validate::validate($interval_value, 'interval_value', '/^([0-9]+)$/Di', 'stringisempty', [], true);
|
||||||
$interval_interval = Validate::validate($interval_interval, 'interval_interval', '', '', [], true);
|
$interval_interval = Validate::validate($interval_interval, 'interval_interval', '', '', [], true);
|
||||||
|
|
||||||
|
if (!in_array(strtoupper($interval_interval), $this->allowed_intervals)) {
|
||||||
|
Response::standardError('invalidcronjobintervalvalue', implode(", ", $this->allowed_intervals), true);
|
||||||
|
}
|
||||||
|
|
||||||
// put together interval value
|
// put together interval value
|
||||||
$interval = $interval_value . ' ' . strtoupper($interval_interval);
|
$interval = $interval_value . ' ' . strtoupper($interval_interval);
|
||||||
|
|
||||||
|
|||||||
@@ -157,16 +157,15 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
|||||||
* this functions validates a given value as ErrorDocument
|
* this functions validates a given value as ErrorDocument
|
||||||
* refs #267
|
* refs #267
|
||||||
*
|
*
|
||||||
* @param
|
* @param string $errdoc
|
||||||
* string error-document-string
|
|
||||||
* @param bool $throw_exception
|
* @param bool $throw_exception
|
||||||
*
|
*
|
||||||
* @return string error-document-string
|
* @return string error-document-string
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private function correctErrorDocument($errdoc = null, $throw_exception = false)
|
private function correctErrorDocument(string $errdoc, $throw_exception = false)
|
||||||
{
|
{
|
||||||
if ($errdoc !== null && $errdoc != '') {
|
if (trim($errdoc) != '') {
|
||||||
// not a URL
|
// not a URL
|
||||||
if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) {
|
if ((strtoupper(substr($errdoc, 0, 5)) != 'HTTP:' && strtoupper(substr($errdoc, 0, 6)) != 'HTTPS:') || !Validate::validateUrl($errdoc)) {
|
||||||
// a file
|
// a file
|
||||||
@@ -176,14 +175,14 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
|||||||
if (!substr($errdoc, 0, 1) == '/') {
|
if (!substr($errdoc, 0, 1) == '/') {
|
||||||
$errdoc = '/' . $errdoc;
|
$errdoc = '/' . $errdoc;
|
||||||
}
|
}
|
||||||
} else {
|
} elseif (preg_match('/^"([^\r\n\t\f\0"]+)"$/', $errdoc)) {
|
||||||
// a string (check for ending ")
|
// a string (check for ending ")
|
||||||
// string won't work for lighty
|
// string won't work for lighty
|
||||||
if (Settings::Get('system.webserver') == 'lighttpd') {
|
if (Settings::Get('system.webserver') == 'lighttpd') {
|
||||||
Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception);
|
Response::standardError('stringerrordocumentnotvalidforlighty', '', $throw_exception);
|
||||||
} elseif (substr($errdoc, -1) != '"') {
|
|
||||||
$errdoc .= '"';
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Response::standardError('invaliderrordocumentvalue', '', $throw_exception);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Settings::Get('system.webserver') == 'lighttpd') {
|
if (Settings::Get('system.webserver') == 'lighttpd') {
|
||||||
@@ -191,7 +190,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $errdoc;
|
return trim($errdoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
|||||||
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path);
|
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path);
|
||||||
$username = Validate::validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', [], true);
|
$username = Validate::validate($username, 'username', '/^[a-zA-Z0-9][a-zA-Z0-9\-_]+\$?$/', '', [], true);
|
||||||
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
||||||
Validate::validate($password, 'password', '', '', [], true);
|
$password = Validate::validate($password, 'password', '', '', [], true);
|
||||||
|
$password = Crypt::validatePassword($password, true);
|
||||||
|
|
||||||
// check for duplicate usernames for the path
|
// check for duplicate usernames for the path
|
||||||
$username_path_check_stmt = Database::prepare("
|
$username_path_check_stmt = Database::prepare("
|
||||||
@@ -244,7 +245,8 @@ class DirProtections extends ApiCommand implements ResourceEntity
|
|||||||
|
|
||||||
// validation
|
// validation
|
||||||
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
$authname = Validate::validate($authname, 'directory_authname', '/^[a-zA-Z0-9][a-zA-Z0-9\-_ ]+\$?$/', '', [], true);
|
||||||
Validate::validate($password, 'password', '', '', [], true);
|
$password = Validate::validate($password, 'password', '', '', [], true);
|
||||||
|
$password = Crypt::validatePassword($password, true);
|
||||||
|
|
||||||
$upd_query = "";
|
$upd_query = "";
|
||||||
$upd_params = [
|
$upd_params = [
|
||||||
|
|||||||
@@ -246,7 +246,10 @@ final class InstallCommand extends Command
|
|||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->io->error($e->getMessage());
|
$this->io->error($e->getMessage());
|
||||||
return $this->showStep($step, $extended, $decoded_input);
|
if ($this->io->confirm('Retry?', empty($decoded_input))) {
|
||||||
|
return $this->showStep($step, $extended, $decoded_input);
|
||||||
|
}
|
||||||
|
return self::FAILURE;
|
||||||
}
|
}
|
||||||
if ($step == 3) {
|
if ($step == 3) {
|
||||||
// do actual install with data from $this->formfielddata
|
// do actual install with data from $this->formfielddata
|
||||||
@@ -297,7 +300,7 @@ final class InstallCommand extends Command
|
|||||||
$json_output = [];
|
$json_output = [];
|
||||||
foreach ($fields['install']['sections'] as $section => $section_fields) {
|
foreach ($fields['install']['sections'] as $section => $section_fields) {
|
||||||
foreach ($section_fields['fields'] as $name => $field) {
|
foreach ($section_fields['fields'] as $name => $field) {
|
||||||
if ($name == 'system' || $name == 'manual_config') {
|
if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($field['type'] == 'text' || $field['type'] == 'email') {
|
if ($field['type'] == 'text' || $field['type'] == 'email') {
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ final class ValidateAcmeWebroot extends CliCommand
|
|||||||
$sel_stmt = Database::prepare("SELECT id, domain FROM panel_domains WHERE `letsencrypt` = '1' AND aliasdomain IS NULL ORDER BY id ASC");
|
$sel_stmt = Database::prepare("SELECT id, domain FROM panel_domains WHERE `letsencrypt` = '1' AND aliasdomain IS NULL ORDER BY id ASC");
|
||||||
Database::pexecute($sel_stmt);
|
Database::pexecute($sel_stmt);
|
||||||
$domains = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);
|
$domains = $sel_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
// check for froxlor-vhost
|
||||||
|
if (Settings::Get('system.le_froxlor_enabled') == '1') {
|
||||||
|
$domains[] = [
|
||||||
|
'id' => 0,
|
||||||
|
'domain' => Settings::Get('system.hostname')
|
||||||
|
];
|
||||||
|
}
|
||||||
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET expirationdate=NULL WHERE `domainid` = :did");
|
$upd_stmt = Database::prepare("UPDATE domain_ssl_settings SET expirationdate=NULL WHERE `domainid` = :did");
|
||||||
$acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
|
$acmesh_dir = dirname(Settings::Get('system.acmeshpath'));
|
||||||
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
|
$acmesh_challenge_dir = rtrim(FileDir::makeCorrectDir(Settings::Get('system.letsencryptchallengepath')), "/");
|
||||||
@@ -139,6 +146,7 @@ final class ValidateAcmeWebroot extends CliCommand
|
|||||||
}
|
}
|
||||||
if ($count_changes > 0) {
|
if ($count_changes > 0) {
|
||||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||||
|
$io->info("Changes detected but froxlor has been updated. Inserting task to rebuild vhosts after update.");
|
||||||
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
Cronjob::inserttask(TaskId::REBUILD_VHOST);
|
||||||
} else {
|
} else {
|
||||||
$question = new ConfirmationQuestion('Changes detected. Force cronjob to refresh certificates? [yes] ', true, '/^(y|j)/i');
|
$question = new ConfirmationQuestion('Changes detected. Force cronjob to refresh certificates? [yes] ', true, '/^(y|j)/i');
|
||||||
@@ -146,6 +154,8 @@ final class ValidateAcmeWebroot extends CliCommand
|
|||||||
passthru(FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -f -d');
|
passthru(FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/bin/froxlor-cli') . ' froxlor:cron -f -d');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$io->success("No changes necessary.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ class ConfigDisplay
|
|||||||
if ($lasttype != '' && $lasttype != $_action['type']) {
|
if ($lasttype != '' && $lasttype != $_action['type']) {
|
||||||
$commands = trim($commands);
|
$commands = trim($commands);
|
||||||
$numbrows = count(explode("\n", $commands));
|
$numbrows = count(explode("\n", $commands));
|
||||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||||
'commands' => $commands,
|
'commands' => $commands,
|
||||||
'numbrows' => $numbrows
|
'numbrows' => $numbrows
|
||||||
]);
|
]);
|
||||||
@@ -182,7 +182,7 @@ class ConfigDisplay
|
|||||||
$commands = trim($commands_pre);
|
$commands = trim($commands_pre);
|
||||||
if ($commands != "") {
|
if ($commands != "") {
|
||||||
$numbrows = count(explode("\n", $commands));
|
$numbrows = count(explode("\n", $commands));
|
||||||
$commands_pre = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
$commands_pre = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||||
'commands' => $commands,
|
'commands' => $commands,
|
||||||
'numbrows' => $numbrows
|
'numbrows' => $numbrows
|
||||||
]);
|
]);
|
||||||
@@ -190,12 +190,12 @@ class ConfigDisplay
|
|||||||
$commands = trim($commands_post);
|
$commands = trim($commands_post);
|
||||||
if ($commands != "") {
|
if ($commands != "") {
|
||||||
$numbrows = count(explode("\n", $commands));
|
$numbrows = count(explode("\n", $commands));
|
||||||
$commands_post = UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
$commands_post = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||||
'commands' => $commands,
|
'commands' => $commands,
|
||||||
'numbrows' => $numbrows
|
'numbrows' => $numbrows
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/fileblock.html.twig', [
|
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/fileblock.html.twig', self::$theme), [
|
||||||
'realname' => $realname,
|
'realname' => $realname,
|
||||||
'commands_pre' => $commands_pre,
|
'commands_pre' => $commands_pre,
|
||||||
'commands_file' => $commands_file,
|
'commands_file' => $commands_file,
|
||||||
@@ -210,7 +210,7 @@ class ConfigDisplay
|
|||||||
$commands = trim($commands);
|
$commands = trim($commands);
|
||||||
if ($commands != '') {
|
if ($commands != '') {
|
||||||
$numbrows = count(explode("\n", $commands));
|
$numbrows = count(explode("\n", $commands));
|
||||||
$configpage .= UI::twig()->render(self::$theme . '/settings/conf/command.html.twig', [
|
$configpage .= UI::twig()->render(UI::validateThemeTemplate('/settings/conf/command.html.twig', self::$theme), [
|
||||||
'commands' => $commands,
|
'commands' => $commands,
|
||||||
'numbrows' => $numbrows
|
'numbrows' => $numbrows
|
||||||
]);
|
]);
|
||||||
@@ -233,7 +233,7 @@ class ConfigDisplay
|
|||||||
$file_content = htmlspecialchars($file_content);
|
$file_content = htmlspecialchars($file_content);
|
||||||
$numbrows = count(explode("\n", $file_content));
|
$numbrows = count(explode("\n", $file_content));
|
||||||
//eval("\$files=\"" . \Froxlor\UI\Template::getTemplate("configfiles/configfiles_file") . "\";");
|
//eval("\$files=\"" . \Froxlor\UI\Template::getTemplate("configfiles/configfiles_file") . "\";");
|
||||||
$files = UI::twig()->render(self::$theme . '/settings/conf/file.html.twig', [
|
$files = UI::twig()->render(UI::validateThemeTemplate('/settings/conf/file.html.twig', self::$theme), [
|
||||||
'distro_editor' => self::$editor,
|
'distro_editor' => self::$editor,
|
||||||
'realname' => $realname,
|
'realname' => $realname,
|
||||||
'numbrows' => $numbrows,
|
'numbrows' => $numbrows,
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ class FileDir
|
|||||||
*/
|
*/
|
||||||
public static function makeSecurePath($path)
|
public static function makeSecurePath($path)
|
||||||
{
|
{
|
||||||
// check for bad characters, some are allowed with escaping
|
// check for bad characters, some are allowed with escaping,
|
||||||
// but we generally don't want them in our directory-names,
|
// but we generally don't want them in our directory-names,
|
||||||
// thx to aaronmueller for this snipped
|
// thx to aaronmueller for this snippet
|
||||||
$badchars = [
|
$badchars = [
|
||||||
':',
|
':',
|
||||||
';',
|
';',
|
||||||
@@ -161,7 +161,11 @@ class FileDir
|
|||||||
'$',
|
'$',
|
||||||
'~',
|
'~',
|
||||||
'?',
|
'?',
|
||||||
"\0"
|
"\0",
|
||||||
|
"\n",
|
||||||
|
"\r",
|
||||||
|
"\t",
|
||||||
|
"\f"
|
||||||
];
|
];
|
||||||
foreach ($badchars as $bc) {
|
foreach ($badchars as $bc) {
|
||||||
$path = str_replace($bc, "", $path);
|
$path = str_replace($bc, "", $path);
|
||||||
@@ -606,7 +610,7 @@ class FileDir
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return array|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
public static function getFilesystemQuota()
|
public static function getFilesystemQuota()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ final class Froxlor
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Main version variable
|
// Main version variable
|
||||||
const VERSION = '2.0.9';
|
const VERSION = '2.0.10';
|
||||||
|
|
||||||
// Database version (YYYYMMDDC where C is a daily counter)
|
// Database version (YYYYMMDDC where C is a daily counter)
|
||||||
const DBVERSION = '202301180';
|
const DBVERSION = '202301180';
|
||||||
|
|||||||
@@ -258,23 +258,27 @@ class PhpHelper
|
|||||||
// set the default nameservers to use, use the system default if none are provided
|
// set the default nameservers to use, use the system default if none are provided
|
||||||
$resolver = new Net_DNS2_Resolver($nameserver ? ['nameservers' => [$nameserver]] : []);
|
$resolver = new Net_DNS2_Resolver($nameserver ? ['nameservers' => [$nameserver]] : []);
|
||||||
|
|
||||||
// get all ip addresses from the A record
|
// get all ip addresses from the A record and normalize them
|
||||||
if ($try_a) {
|
if ($try_a) {
|
||||||
try {
|
try {
|
||||||
$answer = $resolver->query($host, 'A')->answer;
|
$answer = $resolver->query($host, 'A')->answer;
|
||||||
foreach ($answer as $rr) {
|
foreach ($answer as $rr) {
|
||||||
$ips[] = $rr->address;
|
if ($rr instanceof Net_DNS2_RR_A) {
|
||||||
|
$ips[] = inet_ntop(inet_pton($rr->address));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Net_DNS2_Exception $e) {
|
} catch (Net_DNS2_Exception $e) {
|
||||||
// we can't do anything here, just continue
|
// we can't do anything here, just continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all ip addresses from the AAAA record
|
// get all ip addresses from the AAAA record and normalize them
|
||||||
try {
|
try {
|
||||||
$answer = $resolver->query($host, 'AAAA')->answer;
|
$answer = $resolver->query($host, 'AAAA')->answer;
|
||||||
foreach ($answer as $rr) {
|
foreach ($answer as $rr) {
|
||||||
$ips[] = $rr->address;
|
if ($rr instanceof Net_DNS2_RR_AAAA) {
|
||||||
|
$ips[] = inet_ntop(inet_pton($rr->address));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Net_DNS2_Exception $e) {
|
} catch (Net_DNS2_Exception $e) {
|
||||||
// we can't do anything here, just continue
|
// we can't do anything here, just continue
|
||||||
@@ -284,18 +288,18 @@ class PhpHelper
|
|||||||
// problems if the system's dns is not configured correctly; for example, the acme pre-check
|
// problems if the system's dns is not configured correctly; for example, the acme pre-check
|
||||||
// will fail because some providers put a local ip in /etc/hosts
|
// will fail because some providers put a local ip in /etc/hosts
|
||||||
|
|
||||||
// get all ip addresses from the A record
|
// get all ip addresses from the A record and normalize them
|
||||||
if ($try_a) {
|
if ($try_a) {
|
||||||
$answer = @dns_get_record($host, DNS_A);
|
$answer = @dns_get_record($host, DNS_A);
|
||||||
foreach ($answer as $rr) {
|
foreach ($answer as $rr) {
|
||||||
$ips[] = $rr['ip'];
|
$ips[] = inet_ntop(inet_pton($rr['ip']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all ip addresses from the AAAA record
|
// get all ip addresses from the AAAA record and normalize them
|
||||||
$answer = @dns_get_record($host, DNS_AAAA);
|
$answer = @dns_get_record($host, DNS_AAAA);
|
||||||
foreach ($answer as $rr) {
|
foreach ($answer as $rr) {
|
||||||
$ips[] = $rr['ipv6'];
|
$ips[] = inet_ntop(inet_pton($rr['ipv6']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class Text
|
|||||||
$result = $attributes['fields'];
|
$result = $attributes['fields'];
|
||||||
$apikey_data = include Froxlor::getInstallDir() . '/lib/formfields/formfield.api_key.php';
|
$apikey_data = include Froxlor::getInstallDir() . '/lib/formfields/formfield.api_key.php';
|
||||||
|
|
||||||
$body = UI::twig()->render(UI::getTheme() . '/user/inline-form.html.twig', [
|
$body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [
|
||||||
'formaction' => $linker->getLink(['section' => 'index', 'page' => 'apikeys']),
|
'formaction' => $linker->getLink(['section' => 'index', 'page' => 'apikeys']),
|
||||||
'formdata' => $apikey_data['apikey'],
|
'formdata' => $apikey_data['apikey'],
|
||||||
'editid' => $attributes['fields']['id']
|
'editid' => $attributes['fields']['id']
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class UI
|
|||||||
session_set_cookie_params([
|
session_set_cookie_params([
|
||||||
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
|
'lifetime' => self::$install_mode ? 7200 : 600, // will be renewed based on settings in lib/init.php
|
||||||
'path' => '/',
|
'path' => '/',
|
||||||
'domain' => $_SERVER['SERVER_NAME'],
|
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0],
|
||||||
'secure' => self::requestIsHttps(),
|
'secure' => self::requestIsHttps(),
|
||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Strict'
|
'samesite' => 'Strict'
|
||||||
@@ -260,7 +260,18 @@ class UI
|
|||||||
*/
|
*/
|
||||||
public static function twigBuffer($name, array $context = [])
|
public static function twigBuffer($name, array $context = [])
|
||||||
{
|
{
|
||||||
$template_file = self::getTheme() . '/' . $name;
|
$template_file = self::validateThemeTemplate($name);
|
||||||
|
|
||||||
|
self::$twigbuf[] = [
|
||||||
|
$template_file => $context
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validateThemeTemplate(string $name, string $theme = "") {
|
||||||
|
if (empty(trim($theme))) {
|
||||||
|
$theme = self::getTheme();
|
||||||
|
}
|
||||||
|
$template_file = $theme . '/' . $name;
|
||||||
if (!file_exists(Froxlor::getInstallDir() . '/templates/' . $template_file)) {
|
if (!file_exists(Froxlor::getInstallDir() . '/templates/' . $template_file)) {
|
||||||
PhpHelper::phpErrHandler(E_USER_WARNING, "Template '" . $template_file . "' could not be found, trying fallback theme", __FILE__, __LINE__);
|
PhpHelper::phpErrHandler(E_USER_WARNING, "Template '" . $template_file . "' could not be found, trying fallback theme", __FILE__, __LINE__);
|
||||||
$template_file = self::$default_theme . '/'. $name;
|
$template_file = self::$default_theme . '/'. $name;
|
||||||
@@ -268,10 +279,7 @@ class UI
|
|||||||
PhpHelper::phpErrHandler(E_USER_ERROR, "Unknown template '" . $template_file . "'", __FILE__, __LINE__);
|
PhpHelper::phpErrHandler(E_USER_ERROR, "Unknown template '" . $template_file . "'", __FILE__, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return $template_file;
|
||||||
self::$twigbuf[] = [
|
|
||||||
$template_file => $context
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTheme()
|
public static function getTheme()
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ if (CurrentUser::hasSession()) {
|
|||||||
$cookie_params = [
|
$cookie_params = [
|
||||||
'expires' => time() + Settings::Get('session.sessiontimeout'),
|
'expires' => time() + Settings::Get('session.sessiontimeout'),
|
||||||
'path' => '/',
|
'path' => '/',
|
||||||
'domain' => $_SERVER['SERVER_NAME'],
|
'domain' => explode(':', $_SERVER['HTTP_HOST'])[0],
|
||||||
'secure' => UI::requestIsHttps(),
|
'secure' => UI::requestIsHttps(),
|
||||||
'httponly' => true,
|
'httponly' => true,
|
||||||
'samesite' => 'Strict'
|
'samesite' => 'Strict'
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ return [
|
|||||||
'class' => 'text-center',
|
'class' => 'text-center',
|
||||||
'callback' => [Text::class, 'boolean'],
|
'callback' => [Text::class, 'boolean'],
|
||||||
],
|
],
|
||||||
|
'c.lastlogin_succ' => [
|
||||||
|
'label' => lng('admin.lastlogin_succ'),
|
||||||
|
'field' => 'lastlogin_succ',
|
||||||
|
'callback' => [Text::class, 'timestamp'],
|
||||||
|
],
|
||||||
'c.phpenabled' => [
|
'c.phpenabled' => [
|
||||||
'label' => lng('admin.phpenabled'),
|
'label' => lng('admin.phpenabled'),
|
||||||
'field' => 'phpenabled',
|
'field' => 'phpenabled',
|
||||||
@@ -138,7 +143,7 @@ return [
|
|||||||
'class' => 'text-center',
|
'class' => 'text-center',
|
||||||
'callback' => [Text::class, 'boolean'],
|
'callback' => [Text::class, 'boolean'],
|
||||||
],
|
],
|
||||||
'api_allowed' => [
|
'c.api_allowed' => [
|
||||||
'label' => lng('usersettings.api_allowed.title'),
|
'label' => lng('usersettings.api_allowed.title'),
|
||||||
'field' => 'api_allowed',
|
'field' => 'api_allowed',
|
||||||
'class' => 'text-center',
|
'class' => 'text-center',
|
||||||
|
|||||||
@@ -773,6 +773,7 @@ return [
|
|||||||
'destinationiswrong' => 'Die Weiterleitungsadresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.',
|
'destinationiswrong' => 'Die Weiterleitungsadresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.',
|
||||||
'backupfoldercannotbedocroot' => 'Der Ordner für Backups darf nicht das Heimatverzeichnis sein, wählen Sie einen Ordner unterhalb des Heimatverzeichnisses, z.B. /backups',
|
'backupfoldercannotbedocroot' => 'Der Ordner für Backups darf nicht das Heimatverzeichnis sein, wählen Sie einen Ordner unterhalb des Heimatverzeichnisses, z.B. /backups',
|
||||||
'templatelanguagecombodefined' => 'Die gewählte Kombination aus Sprache und Vorlage ist bereits definiert.',
|
'templatelanguagecombodefined' => 'Die gewählte Kombination aus Sprache und Vorlage ist bereits definiert.',
|
||||||
|
'templatelanguageinvalid' => 'Die gewählte Sprache existiert nicht',
|
||||||
'ipstillhasdomains' => 'Die IP/Port-Kombination, die Sie löschen wollen, ist noch bei einer oder mehreren Domains eingetragen. Bitte ändern Sie die Domains vorher auf eine andere IP/Port-Kombination, um diese löschen zu können.',
|
'ipstillhasdomains' => 'Die IP/Port-Kombination, die Sie löschen wollen, ist noch bei einer oder mehreren Domains eingetragen. Bitte ändern Sie die Domains vorher auf eine andere IP/Port-Kombination, um diese löschen zu können.',
|
||||||
'cantdeletedefaultip' => 'Sie können die Standard-IP/Port-Kombination für Reseller nicht löschen. Bitte setzen Sie eine andere IP/Port-Kombination als Standard, um diese löschen zu können.',
|
'cantdeletedefaultip' => 'Sie können die Standard-IP/Port-Kombination für Reseller nicht löschen. Bitte setzen Sie eine andere IP/Port-Kombination als Standard, um diese löschen zu können.',
|
||||||
'cantdeletesystemip' => 'Sie können die letzte System-IP-Adresse nicht löschen. Entweder legen Sie eine neue IP/Port-Kombination an oder Sie ändern die System-IP-Adresse.',
|
'cantdeletesystemip' => 'Sie können die letzte System-IP-Adresse nicht löschen. Entweder legen Sie eine neue IP/Port-Kombination an oder Sie ändern die System-IP-Adresse.',
|
||||||
@@ -836,6 +837,7 @@ return [
|
|||||||
'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',
|
'notrequiredpasswordcomplexity' => 'Die vorgegebene Passwort-Komplexität wurde nicht erfüllt.<br />Bitte kontaktieren Sie Ihren Administrator, wenn Sie Fragen zur Komplexitäts-Vorgabe haben.',
|
||||||
'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
'stringerrordocumentnotvalidforlighty' => 'Ein Text als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
||||||
'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
'urlerrordocumentnotvalidforlighty' => 'Eine URL als Fehlerdokument funktioniert leider in LigHTTPd nicht, bitte geben Sie einen Pfad zu einer Datei an',
|
||||||
|
'invaliderrordocumentvalue' => 'Der angegebene Wert für das Fehlederdokument ist keine gültige Datei, URL oder Text-Zeile.',
|
||||||
'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld "%s")',
|
'intvaluetoolow' => 'Die angegebene Zahl ist zu klein (Feld "%s")',
|
||||||
'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld "%s")',
|
'intvaluetoohigh' => 'Die angegebene Zahl ist zu groß (Feld "%s")',
|
||||||
'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren',
|
'phpfpmstillenabled' => 'PHP-FPM ist derzeit aktiviert. Bitte deaktivieren Sie es, um FCGID zu aktivieren',
|
||||||
@@ -919,6 +921,7 @@ return [
|
|||||||
'pathmustberelative' => 'Der Benutzer hat nicht die benötigten Berechtigungen, um Pfade außerhalb des Kunden-Heimatverzeichnisses anzugeben. Bitte einen relativen Pfad angeben (kein führendes /).',
|
'pathmustberelative' => 'Der Benutzer hat nicht die benötigten Berechtigungen, um Pfade außerhalb des Kunden-Heimatverzeichnisses anzugeben. Bitte einen relativen Pfad angeben (kein führendes /).',
|
||||||
'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.',
|
'mysqlserverstillhasdbs' => 'Datenbank-Server kann für den Kunden nicht entfernt werden, da sich dort noch Datenbanken befinden.',
|
||||||
'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
|
'domaincannotbeedited' => 'Keine Berechtigung, um die Domain %s zu bearbeiten',
|
||||||
|
'invalidcronjobintervalvalue' => 'Cronjob Intervall muss einer der folgenden Werte sein: %s',
|
||||||
],
|
],
|
||||||
'extras' => [
|
'extras' => [
|
||||||
'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',
|
'description' => 'Hier können Sie zusätzliche Extras einrichten, wie zum Beispiel einen Verzeichnisschutz.<br />Die Änderungen sind erst nach einer kurzen Zeit wirksam.',
|
||||||
|
|||||||
@@ -839,6 +839,7 @@ return [
|
|||||||
'destinationiswrong' => 'The forwarder %s contains invalid character(s) or is incomplete.',
|
'destinationiswrong' => 'The forwarder %s contains invalid character(s) or is incomplete.',
|
||||||
'backupfoldercannotbedocroot' => 'The folder for backups cannot be your homedir, please chose a folder within your homedir, e.g. /backups',
|
'backupfoldercannotbedocroot' => 'The folder for backups cannot be your homedir, please chose a folder within your homedir, e.g. /backups',
|
||||||
'templatelanguagecombodefined' => 'The selected language/template combination has already been defined.',
|
'templatelanguagecombodefined' => 'The selected language/template combination has already been defined.',
|
||||||
|
'templatelanguageinvalid' => 'The selected language does not exist',
|
||||||
'ipstillhasdomains' => 'The IP/Port combination you want to delete still has domains assigned to it, please reassign those to other IP/Port combinations before deleting this IP/Port combination.',
|
'ipstillhasdomains' => 'The IP/Port combination you want to delete still has domains assigned to it, please reassign those to other IP/Port combinations before deleting this IP/Port combination.',
|
||||||
'cantdeletedefaultip' => 'You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.',
|
'cantdeletedefaultip' => 'You cannot delete the default IP/Port combination, please make another IP/Port combination default for before deleting this IP/Port combination.',
|
||||||
'cantdeletesystemip' => 'You cannot delete the last system IP, either create a new IP/Port combination for the system IP or change the system IP.',
|
'cantdeletesystemip' => 'You cannot delete the last system IP, either create a new IP/Port combination for the system IP or change the system IP.',
|
||||||
@@ -904,6 +905,7 @@ return [
|
|||||||
'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',
|
'notrequiredpasswordcomplexity' => 'The specified password-complexity was not satisfied.<br />Please contact your administrator if you have any questions about the complexity-specification',
|
||||||
'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
'stringerrordocumentnotvalidforlighty' => 'A string as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
||||||
'urlerrordocumentnotvalidforlighty' => 'An URL as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
'urlerrordocumentnotvalidforlighty' => 'An URL as ErrorDocument does not work in lighttpd, please specify a path to a file',
|
||||||
|
'invaliderrordocumentvalue' => 'The value given as ErrorDocument does not seem to be a valid file, URL or string.',
|
||||||
'intvaluetoolow' => 'The given number is too low (field %s)',
|
'intvaluetoolow' => 'The given number is too low (field %s)',
|
||||||
'intvaluetoohigh' => 'The given number is too high (field %s)',
|
'intvaluetoohigh' => 'The given number is too high (field %s)',
|
||||||
'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID',
|
'phpfpmstillenabled' => 'PHP-FPM is currently active. Please deactivate it before activating FCGID',
|
||||||
@@ -988,6 +990,7 @@ return [
|
|||||||
'pathmustberelative' => 'The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).',
|
'pathmustberelative' => 'The user does not have the permission to specify directories outside the customers home-directory. Please specify a relative path (no leading /).',
|
||||||
'mysqlserverstillhasdbs' => 'Cannot remove database server from customers allow-list as there are still databases on it.',
|
'mysqlserverstillhasdbs' => 'Cannot remove database server from customers allow-list as there are still databases on it.',
|
||||||
'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
|
'domaincannotbeedited' => 'You are not permitted to edit the domain %s',
|
||||||
|
'invalidcronjobintervalvalue' => 'Cronjob interval must be one of: %s',
|
||||||
],
|
],
|
||||||
'extras' => [
|
'extras' => [
|
||||||
'description' => 'Here you can add some extras, for example directory protection.<br />The system will need some time to apply the new settings after every change.',
|
'description' => 'Here you can add some extras, for example directory protection.<br />The system will need some time to apply the new settings after every change.',
|
||||||
|
|||||||
@@ -191,4 +191,49 @@ class DirOptionsTest extends TestCase
|
|||||||
$this->expectExceptionMessage("Directory option with id #1 could not be found");
|
$this->expectExceptionMessage("Directory option with id #1 could not be found");
|
||||||
DirOptions::getLocal($admin_userdata, $data)->get();
|
DirOptions::getLocal($admin_userdata, $data)->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCustomerDirOptionsAddMalformed()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
// get customer
|
||||||
|
$json_result = Customers::getLocal($admin_userdata, array(
|
||||||
|
'loginname' => 'test1'
|
||||||
|
))->get();
|
||||||
|
$customer_userdata = json_decode($json_result, true)['data'];
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'path' => '/testmalformed',
|
||||||
|
'error404path' => '/"'.PHP_EOL.'something/../../../../weird 404.html'.PHP_EOL.'#'
|
||||||
|
];
|
||||||
|
$json_result = DirOptions::getLocal($customer_userdata, $data)->add();
|
||||||
|
$result = json_decode($json_result, true)['data'];
|
||||||
|
$expected = '/"something/././././weird\ 404.html#';
|
||||||
|
$this->assertEquals($expected, $result['error404path']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCustomerDirOptionsAddMalformedInvalid()
|
||||||
|
{
|
||||||
|
global $admin_userdata;
|
||||||
|
|
||||||
|
// get customer
|
||||||
|
$json_result = Customers::getLocal($admin_userdata, array(
|
||||||
|
'loginname' => 'test1'
|
||||||
|
))->get();
|
||||||
|
$customer_userdata = json_decode($json_result, true)['data'];
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'path' => '/testmalformed',
|
||||||
|
'error404path' => '"'.PHP_EOL.'IncludeOptional /something/else/'.PHP_EOL.'#'
|
||||||
|
];
|
||||||
|
$this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string.");
|
||||||
|
DirOptions::getLocal($customer_userdata, $data)->add();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'path' => '/testmalformed',
|
||||||
|
'error404path' => '"something"oh no a quote within the string"'
|
||||||
|
];
|
||||||
|
$this->expectExceptionMessage("The value given as ErrorDocument does not seem to be a valid file, URL or string.");
|
||||||
|
DirOptions::getLocal($customer_userdata, $data)->add();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user