Merge remote-tracking branch 'origin/2.1.x' into feature-backup

This commit is contained in:
Maurice Preuß (envoyr)
2023-06-06 14:41:41 +02:00
86 changed files with 818 additions and 448 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ logs/*
*~ *~
.well-known .well-known
.idea .idea
.DS_Store
*.iml *.iml
img/ img/
vendor/ vendor/

View File

@@ -28,7 +28,7 @@ require __DIR__ . '/lib/init.php';
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Http\HttpClient; use Froxlor\FileDir;
use Froxlor\Install\AutoUpdate; use Froxlor\Install\AutoUpdate;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
@@ -132,7 +132,7 @@ elseif ($page == 'getdownload') {
elseif ($page == 'extract') { elseif ($page == 'extract') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (isset($_POST['send']) && $_POST['send'] == 'send') {
$toExtract = isset($_POST['archive']) ? $_POST['archive'] : null; $toExtract = isset($_POST['archive']) ? $_POST['archive'] : null;
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract; $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir()); $log->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "Extracting " . $localArchive . " to " . Froxlor::getInstallDir());
$result = AutoUpdate::extractZip($localArchive); $result = AutoUpdate::extractZip($localArchive);
if ($result > 0) { if ($result > 0) {
@@ -146,7 +146,7 @@ elseif ($page == 'extract') {
Response::redirectTo('admin_updates.php'); Response::redirectTo('admin_updates.php');
} else { } else {
$toExtract = isset($_GET['archive']) ? $_GET['archive'] : null; $toExtract = isset($_GET['archive']) ? $_GET['archive'] : null;
$localArchive = Froxlor::getInstallDir() . '/updates/' . $toExtract; $localArchive = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/updates/' . $toExtract);
} }
if (!file_exists($localArchive)) { if (!file_exists($localArchive)) {

View File

@@ -114,15 +114,11 @@ if ($page == 'domains' || $page == 'overview') {
} elseif ($alias_check['count'] > 0) { } elseif ($alias_check['count'] > 0) {
Response::standardError('domains_cantdeletedomainwithaliases'); Response::standardError('domains_cantdeletedomainwithaliases');
} else { } else {
$showcheck = false; HTML::askYesNo('admin_domain_reallydelete', $filename, [
if (Domain::domainHasMainSubDomains($id)) {
$showcheck = true;
}
HTML::askYesNoWithCheckbox('admin_domain_reallydelete', 'remove_subbutmain_domains', $filename, [
'id' => $id, 'id' => $id,
'page' => $page, 'page' => $page,
'action' => $action 'action' => $action
], $idna_convert->decode($result['domain']), $showcheck); ], $idna_convert->decode($result['domain']));
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
@@ -252,21 +248,6 @@ if ($page == 'domains' || $page == 'overview') {
$domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')'; $domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')';
} }
$subtodomains = [
0 => lng('domains.nosubtomaindomain')
];
$result_domains_stmt = Database::prepare("
SELECT `d`.`id`, `d`.`domain`, `c`.`loginname` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c`
WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = 0 AND `d`.`ismainbutsubto` = 0 " . $standardsubdomains . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . "
AND `d`.`customerid`=`c`.`customerid` ORDER BY `loginname`, `domain` ASC
");
// params from above still valid
Database::pexecute($result_domains_stmt, $params);
while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
$subtodomains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']) . ' (' . $row_domain['loginname'] . ')';
}
$phpconfigs = []; $phpconfigs = [];
$configs = Database::query(" $configs = Database::query("
SELECT c.*, fc.description as interpreter SELECT c.*, fc.description as interpreter
@@ -287,7 +268,7 @@ if ($page == 'domains' || $page == 'overview') {
1 => lng('domain.homedir'), 1 => lng('domain.homedir'),
2 => lng('domain.docparent') 2 => lng('domain.docparent')
]; ];
// create serveralias options // create serveralias options
$serveraliasoptions = [ $serveraliasoptions = [
0 => lng('domains.serveraliasoption_wildcard'), 0 => lng('domains.serveraliasoption_wildcard'),
@@ -469,27 +450,6 @@ if ($page == 'domains' || $page == 'overview') {
$domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']); $domains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']);
} }
$subtodomains = [
0 => lng('domains.nosubtomaindomain')
];
$result_domains_stmt = Database::prepare("
SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c`
WHERE `d`.`aliasdomain` IS NULL AND `d`.`parentdomainid` = '0' AND `d`.`id` <> :id
AND `c`.`standardsubdomain`<>`d`.`id` AND `c`.`customerid`=`d`.`customerid`" . ($userinfo['customers_see_all'] ? '' : " AND `d`.`adminid` = :adminid") . "
ORDER BY `d`.`domain` ASC
");
$params = [
'id' => $result['id']
];
if ($userinfo['customers_see_all'] == '0') {
$params['adminid'] = $userinfo['adminid'];
}
Database::pexecute($result_domains_stmt, $params);
while ($row_domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
$subtodomains[$row_domain['id']] = $idna_convert->decode($row_domain['domain']);
}
if ($userinfo['ip'] == "-1") { if ($userinfo['ip'] == "-1") {
$result_ipsandports_stmt = Database::query(" $result_ipsandports_stmt = Database::query("
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `ssl`='0' ORDER BY `ip`, `port` ASC
@@ -556,7 +516,7 @@ if ($page == 'domains' || $page == 'overview') {
1 => lng('domain.homedir'), 1 => lng('domain.homedir'),
2 => lng('domain.docparent') 2 => lng('domain.docparent')
]; ];
$serveraliasoptions = [ $serveraliasoptions = [
0 => lng('domains.serveraliasoption_wildcard'), 0 => lng('domains.serveraliasoption_wildcard'),
1 => lng('domains.serveraliasoption_www'), 1 => lng('domains.serveraliasoption_www'),
@@ -676,6 +636,23 @@ if ($page == 'domains' || $page == 'overview') {
'alert_msg' => lng('domains.import_description') 'alert_msg' => lng('domains.import_description')
]); ]);
} }
} elseif ($action == 'duplicate') {
if (isset($_POST['send']) && $_POST['send'] == 'send') {
try {
Domains::getLocal($userinfo, $_POST)->duplicate();
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
Response::redirectTo($filename, [
'page' => $page,
'searchfield' => 'd.domain_ace',
'searchtext' => $_POST['domain'] ?? ""
]);
} else {
Response::redirectTo($filename, [
'page' => 'overview'
]);
}
} }
} elseif ($page == 'domainssleditor') { } elseif ($page == 'domainssleditor') {
require_once __DIR__ . '/ssl_editor.php'; require_once __DIR__ . '/ssl_editor.php';

View File

@@ -51,7 +51,7 @@ $id = (int)Request::any('id');
if ($page == 'overview' || $page == 'domains') { if ($page == 'overview' || $page == 'domains') {
if ($action == '') { if ($action == '') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_domains::domains"); $log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "viewed customer_domains::domains");
$parentdomain_id = (int)Request::any('pid', '0'); $parentdomain_id = (int)Request::any('pid', '0');
@@ -73,10 +73,17 @@ if ($page == 'overview' || $page == 'domains') {
]; ];
} }
UI::view('user/table.html.twig', [ $table_tpl = 'table.html.twig';
if ($collection->count() == 0) {
$table_tpl = 'table-note.html.twig';
}
UI::view('user/' . $table_tpl, [
'listing' => Listing::format($collection, $domain_list_data, 'domain_list'), 'listing' => Listing::format($collection, $domain_list_data, 'domain_list'),
'actions_links' => $actions_links, 'actions_links' => $actions_links,
'entity_info' => lng('domains.description') 'entity_info' => lng('domains.description'),
// alert-box
'type' => 'warning',
'alert_msg' => lng('domains.nodomainsassignedbyadmin')
]); ]);
} elseif ($action == 'delete' && $id != 0) { } elseif ($action == 'delete' && $id != 0) {
try { try {
@@ -130,6 +137,7 @@ if ($page == 'overview' || $page == 'domains') {
AND `parentdomainid` = '0' AND `parentdomainid` = '0'
AND `email_only` = '0' AND `email_only` = '0'
AND `caneditdomain` = '1' AND `caneditdomain` = '1'
AND `deactivated` = '0'
ORDER BY `domain` ASC"); ORDER BY `domain` ASC");
Database::pexecute($stmt, [ Database::pexecute($stmt, [
"customerid" => $userinfo['customerid'] "customerid" => $userinfo['customerid']
@@ -139,6 +147,14 @@ if ($page == 'overview' || $page == 'domains') {
$domains[$row['domain']] = $idna_convert->decode($row['domain']); $domains[$row['domain']] = $idna_convert->decode($row['domain']);
} }
// check of there are any domains to be used
if (count($domains) <= 0) {
// no, possible direct URL access, redirect to overview
Response::redirectTo($filename, [
'page' => $page
]);
}
$aliasdomains[0] = lng('domains.noaliasdomain'); $aliasdomains[0] = lng('domains.noaliasdomain');
$domains_stmt = Database::prepare("SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c` $domains_stmt = Database::prepare("SELECT `d`.`id`, `d`.`domain` FROM `" . TABLE_PANEL_DOMAINS . "` `d`, `" . TABLE_PANEL_CUSTOMERS . "` `c`
WHERE `d`.`aliasdomain` IS NULL WHERE `d`.`aliasdomain` IS NULL

View File

@@ -84,7 +84,7 @@ if ($page == 'overview' || $page == 'emails') {
if ($page == 'email_domain') { if ($page == 'email_domain') {
$email_domainid = Request::any('domainid', 0); $email_domainid = Request::any('domainid', 0);
if ($action == '') { if ($action == '') {
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails"); $log->logAction(FroxlorLogger::USR_ACTION, LOG_INFO, "viewed customer_email::emails");
$sql_search = []; $sql_search = [];
if ($email_domainid > 0) { if ($email_domainid > 0) {

View File

@@ -26,6 +26,7 @@
const AREA = 'login'; const AREA = 'login';
require __DIR__ . '/lib/init.php'; require __DIR__ . '/lib/init.php';
use Froxlor\Api\FroxlorRPC;
use Froxlor\CurrentUser; use Froxlor\CurrentUser;
use Froxlor\Customer\Customer; use Froxlor\Customer\Customer;
use Froxlor\Database\Database; use Froxlor\Database\Database;
@@ -37,10 +38,10 @@ use Froxlor\PhpHelper;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\System\Crypt; use Froxlor\System\Crypt;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
use Froxlor\UI\Response; use Froxlor\UI\Response;
use Froxlor\User; use Froxlor\User;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use Froxlor\Language;
if ($action == '') { if ($action == '') {
$action = 'login'; $action = 'login';
@@ -161,7 +162,7 @@ if ($action == '2fa_entercode') {
]); ]);
exit(); exit();
} elseif ($action == 'login') { } elseif ($action == 'login') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate($_POST['loginname'], 'loginname');
$password = Validate::validate($_POST['password'], 'password'); $password = Validate::validate($_POST['password'], 'password');
@@ -431,13 +432,13 @@ if ($action == '2fa_entercode') {
} }
$lastqrystr = ""; $lastqrystr = "";
if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") { if (isset($_REQUEST['qrystr']) && $_REQUEST['qrystr'] != "") {
$lastqrystr = htmlspecialchars($_REQUEST['qrystr'], ENT_QUOTES); $lastqrystr = urlencode($_REQUEST['qrystr']);
} }
$_SESSION['lastscript'] = $lastscript;
$_SESSION['lastqrystr'] = $lastqrystr;
UI::view('login/login.html.twig', [ UI::view('login/login.html.twig', [
'pagetitle' => 'Login', 'pagetitle' => 'Login',
'lastscript' => $lastscript,
'lastqrystr' => $lastqrystr,
'upd_in_progress' => $update_in_progress, 'upd_in_progress' => $update_in_progress,
'message' => $message, 'message' => $message,
'successmsg' => $successmessage 'successmsg' => $successmessage
@@ -449,7 +450,7 @@ if ($action == 'forgotpwd') {
$adminchecked = false; $adminchecked = false;
$message = ''; $message = '';
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$loginname = Validate::validate($_POST['loginname'], 'loginname'); $loginname = Validate::validate($_POST['loginname'], 'loginname');
$email = Validate::validateEmail($_POST['loginemail']); $email = Validate::validateEmail($_POST['loginemail']);
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "` $result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `customernumber`, `firstname`, `name`, `company`, `email`, `loginname`, `def_language`, `deactivated` FROM `" . TABLE_PANEL_CUSTOMERS . "`
@@ -633,7 +634,7 @@ if ($action == 'forgotpwd') {
UI::view('login/fpwd.html.twig', [ UI::view('login/fpwd.html.twig', [
'pagetitle' => lng('login.presend'), 'pagetitle' => lng('login.presend'),
'action' => $action, 'formaction' => 'index.php?action='.$action,
'message' => $message, 'message' => $message,
]); ]);
} }
@@ -656,7 +657,7 @@ if ($action == 'resetpwd') {
$check = substr($activationcode, 40, 10); $check = substr($activationcode, 40, 10);
if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) { if (substr(md5($third . $timestamp), 0, 10) == $check && $timestamp >= time() - 86400) {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (!empty($_POST)) {
$stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "` $stmt = Database::prepare("SELECT `userid`, `admin` FROM `" . TABLE_PANEL_ACTIVATION . "`
WHERE `activationcode` = :activationcode"); WHERE `activationcode` = :activationcode");
$result = Database::pexecute_first($stmt, [ $result = Database::pexecute_first($stmt, [
@@ -730,6 +731,58 @@ if ($action == 'resetpwd') {
} }
} }
// one-time link login
if ($action == 'll') {
if (!Froxlor::hasUpdates() && !Froxlor::hasDbUpdates()) {
$loginname = Request::get('ln');
$hash = Request::get('h');
if ($loginname && $hash) {
$sel_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_LOGINLINKS . "`
WHERE `loginname` = :loginname AND `hash` = :hash
");
try {
$entry = Database::pexecute_first($sel_stmt, ['loginname' => $loginname, 'hash' => $hash]);
} catch (Exception $e) {
$entry = false;
}
if ($entry) {
// delete entry
$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `loginname` = :loginname AND `hash` = :hash");
Database::pexecute($del_stmt, ['loginname' => $loginname, 'hash' => $hash]);
if (time() <= $entry['valid_until']) {
$valid = true;
// validate source ip if specified
if (!empty($entry['allowed_from'])) {
$valid = false;
$ip_list = explode(",", $entry['allowed_from']);
if (FroxlorRPC::validateAllowedFrom($ip_list, $_SERVER['REMOTE_ADDR'])) {
$valid = true;
}
}
if ($valid) {
// login user / select only non-deactivated (in case the user got deactivated after generating the link)
$userinfo_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `loginname`= :loginname AND `deactivated` = 0");
try {
$userinfo = Database::pexecute_first($userinfo_stmt, [
"loginname" => $loginname
]);
} catch (Exception $e) {
$userinfo = false;
}
if ($userinfo) {
$userinfo['userid'] = $userinfo['customerid'];
$userinfo['adminsession'] = 0;
finishLogin($userinfo);
}
}
}
}
}
}
Response::redirectTo('index.php');
}
function finishLogin($userinfo) function finishLogin($userinfo)
{ {
if (isset($userinfo['userid']) && $userinfo['userid'] != '') { if (isset($userinfo['userid']) && $userinfo['userid'] != '') {
@@ -746,29 +799,34 @@ function finishLogin($userinfo)
} }
$qryparams = []; $qryparams = [];
if (isset($_POST['qrystr']) && $_POST['qrystr'] != "") { if (isset($_SESSION['lastqrystr']) && !empty($_SESSION['lastqrystr'])) {
parse_str(urldecode($_POST['qrystr']), $qryparams); parse_str(urldecode($_SESSION['lastqrystr']), $qryparams);
unset($_SESSION['lastqrystr']);
} }
if ($userinfo['adminsession'] == '1') { if ($userinfo['adminsession'] == '1') {
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) { if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
Response::redirectTo('admin_updates.php?page=overview'); Response::redirectTo('admin_updates.php?page=overview');
} else { } else {
if (isset($_POST['script']) && $_POST['script'] != "") { if (isset($_SESSION['lastscript']) && !empty($_SESSION['lastscript'])) {
if (preg_match("/customer\_/", $_POST['script']) === 1) { $lastscript = $_SESSION['lastscript'];
unset($_SESSION['lastscript']);
if (preg_match("/customer\_/", $lastscript) === 1) {
Response::redirectTo('admin_customers.php', [ Response::redirectTo('admin_customers.php', [
"page" => "customers" "page" => "customers"
]); ]);
} else { } else {
Response::redirectTo($_POST['script'], $qryparams); Response::redirectTo($lastscript, $qryparams);
} }
} else { } else {
Response::redirectTo('admin_index.php', $qryparams); Response::redirectTo('admin_index.php', $qryparams);
} }
} }
} else { } else {
if (isset($_POST['script']) && $_POST['script'] != "") { if (isset($_SESSION['lastscript']) && !empty($_SESSION['lastscript'])) {
Response::redirectTo($_POST['script'], $qryparams); $lastscript = $_SESSION['lastscript'];
unset($_SESSION['lastscript']);
Response::redirectTo($lastscript, $qryparams);
} else { } else {
Response::redirectTo('customer_index.php', $qryparams); Response::redirectTo('customer_index.php', $qryparams);
} }

View File

@@ -278,7 +278,6 @@ CREATE TABLE `panel_domains` (
`phpsettingid` INT( 11 ) UNSIGNED NOT NULL DEFAULT '1', `phpsettingid` INT( 11 ) UNSIGNED NOT NULL DEFAULT '1',
`mod_fcgid_starter` int(4) default '-1', `mod_fcgid_starter` int(4) default '-1',
`mod_fcgid_maxrequests` int(4) default '-1', `mod_fcgid_maxrequests` int(4) default '-1',
`ismainbutsubto` int(11) unsigned NOT NULL default '0',
`letsencrypt` tinyint(1) NOT NULL default '0', `letsencrypt` tinyint(1) NOT NULL default '0',
`hsts` varchar(10) NOT NULL default '0', `hsts` varchar(10) NOT NULL default '0',
`hsts_sub` tinyint(1) NOT NULL default '0', `hsts_sub` tinyint(1) NOT NULL default '0',
@@ -555,7 +554,7 @@ opcache.validate_timestamps'),
('system', 'defaultip', '1'), ('system', 'defaultip', '1'),
('system', 'defaultsslip', ''), ('system', 'defaultsslip', ''),
('system', 'phpappendopenbasedir', '/tmp/'), ('system', 'phpappendopenbasedir', '/tmp/'),
('system', 'deactivateddocroot', ''), ('system', 'deactivateddocroot', '/var/www/html/froxlor/templates/misc/deactivated/'),
('system', 'mailpwcleartext', '0'), ('system', 'mailpwcleartext', '0'),
('system', 'last_tasks_run', '000000'), ('system', 'last_tasks_run', '000000'),
('system', 'nameservers', ''), ('system', 'nameservers', ''),
@@ -697,7 +696,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.19'), ('system', 'update_notify_last', '2.0.20'),
('system', 'traffictool', 'goaccess'), ('system', 'traffictool', 'goaccess'),
('system', 'req_limit_per_interval', 60), ('system', 'req_limit_per_interval', 60),
('system', 'req_limit_interval', 60), ('system', 'req_limit_interval', 60),
@@ -744,8 +743,8 @@ 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.19'), ('panel', 'version', '2.0.20'),
('panel', 'db_version', '202304260'); ('panel', 'db_version', '202305240');
DROP TABLE IF EXISTS `panel_tasks`; DROP TABLE IF EXISTS `panel_tasks`;
@@ -1052,4 +1051,13 @@ CREATE TABLE `panel_usercolumns` (
KEY adminid (adminid), KEY adminid (adminid),
KEY customerid (customerid) KEY customerid (customerid)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
DROP TABLE IF EXISTS `panel_loginlinks`;
CREATE TABLE `panel_loginlinks` (
`hash` varchar(500) NOT NULL,
`loginname` varchar(50) NOT NULL,
`valid_until` int(15) NOT NULL,
`allowed_from` text NOT NULL,
UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
FROXLORSQL; FROXLORSQL;

View File

@@ -492,3 +492,37 @@ if (Froxlor::isFroxlorVersion('2.0.18')) {
Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false); Update::showUpdateStep("Updating from 2.0.18 to 2.0.19", false);
Froxlor::updateToVersion('2.0.19'); Froxlor::updateToVersion('2.0.19');
} }
if (Froxlor::isFroxlorVersion('2.0.19')) {
Update::showUpdateStep("Updating from 2.0.19 to 2.0.20", false);
Froxlor::updateToVersion('2.0.20');
}
if (Froxlor::isDatabaseVersion('202304260')) {
Update::showUpdateStep("Cleaning domains table");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP COLUMN `ismainbutsubto`;");
Update::lastStepStatus(0);
Update::showUpdateStep("Creating new tables and fields");
Database::query("DROP TABLE IF EXISTS `panel_loginlinks`;");
$sql = "CREATE TABLE `panel_loginlinks` (
`hash` varchar(500) NOT NULL,
`loginname` varchar(50) NOT NULL,
`valid_until` int(15) NOT NULL,
`allowed_from` text NOT NULL,
UNIQUE KEY `loginname` (`loginname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
Database::query($sql);
Update::lastStepStatus(0);
Update::showUpdateStep("Adjusting setting for deactivated webroot");
$current_deactivated_webroot = Settings::Get('system.deactivateddocroot');
if (empty($current_deactivated_webroot)) {
Settings::Set('system.deactivateddocroot', FileDir::makeCorrectDir(Froxlor::getInstallDir() . '/templates/misc/deactivated/'));
Update::lastStepStatus(0);
} else {
Update::lastStepStatus(1, 'Customized setting, not changing');
}
Froxlor::updateToDbVersion('202305240');
}

View File

@@ -272,7 +272,8 @@ abstract class ApiCommand extends ApiParameter
$ops = [ $ops = [
'<', '<',
'>', '>',
'=' '=',
'<>'
]; ];
$first = true; $first = true;
foreach ($search as $field => $valoper) { foreach ($search as $field => $valoper) {

View File

@@ -95,7 +95,7 @@ class Admins extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) { if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list admins"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list admins");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT * SELECT *
@@ -407,7 +407,7 @@ class Admins extends ApiCommand implements ResourceEntity
]; ];
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get admin '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get admin '" . $result['loginname'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
@@ -705,7 +705,7 @@ class Admins extends ApiCommand implements ResourceEntity
WHERE `adminid` = :adminid WHERE `adminid` = :adminid
"); ");
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] edited admin '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] edited admin '" . $result['loginname'] . "'");
// get all admin-data for return-array // get all admin-data for return-array
$result = $this->apiCall('Admins.get', [ $result = $this->apiCall('Admins.get', [

View File

@@ -97,7 +97,7 @@ class Certificates extends ApiCommand implements ResourceEntity
} }
if (!$has_cert) { if (!$has_cert) {
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ssl-certificate for '" . $domain['domain'] . "'");
$result = $this->apiCall('Certificates.get', [ $result = $this->apiCall('Certificates.get', [
'id' => $domain['id'] 'id' => $domain['id']
]); ]);
@@ -248,7 +248,7 @@ class Certificates extends ApiCommand implements ResourceEntity
$ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); $ssl_ca_file = $this->getParam('ssl_ca_file', true, '');
$ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, '');
$this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false); $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ssl-certificate for '" . $domain['domain'] . "'");
$result = $this->apiCall('Certificates.get', [ $result = $this->apiCall('Certificates.get', [
'id' => $domain['id'] 'id' => $domain['id']
]); ]);
@@ -470,7 +470,7 @@ class Certificates extends ApiCommand implements ResourceEntity
if ($chk['letsencrypt'] == '1') { if ($chk['letsencrypt'] == '1') {
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']); Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $chk['domain']);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] removed ssl-certificate for '" . $chk['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406); throw new Exception("Unable to determine SSL certificate. Maybe no access?", 406);

View File

@@ -147,7 +147,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
// insert task to re-generate the cron.d-file // insert task to re-generate the cron.d-file
Cronjob::inserttask(TaskId::REBUILD_CRON); Cronjob::inserttask(TaskId::REBUILD_CRON);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] cronjob with description '" . $result['module'] . '/' . $result['cronfile'] . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('Cronjobs.get', [ $result = $this->apiCall('Cronjobs.get', [
'id' => $id 'id' => $id
]); ]);
@@ -177,7 +177,7 @@ class Cronjobs extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list cronjobs"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list cronjobs");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit()); SELECT `c`.* FROM `" . TABLE_PANEL_CRONRUNS . "` `c` " . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());

View File

@@ -212,7 +212,7 @@ class CustomerBackups extends ApiCommand implements ResourceEntity
$result[] = $entry; $result[] = $entry;
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list customer-backups"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list customer-backups");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -895,7 +895,7 @@ class Customers extends ApiCommand implements ResourceEntity
$result['dbspace_used'] = 0; $result['dbspace_used'] = 0;
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get customer '" . $result['loginname'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get customer '" . $result['loginname'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'"); $key = ($id > 0 ? "id #" . $id : "loginname '" . $loginname . "'");
@@ -1327,7 +1327,7 @@ class Customers extends ApiCommand implements ResourceEntity
'vu' => $valid_until 'vu' => $valid_until
], true, true); ], true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
@@ -1538,7 +1538,7 @@ class Customers extends ApiCommand implements ResourceEntity
Database::query($admin_update_query); Database::query($admin_update_query);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited user '" . $result['loginname'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited user '" . $result['loginname'] . "'");
/* /*
* move customer to another admin/reseller; #1166 * move customer to another admin/reseller; #1166
@@ -1911,7 +1911,7 @@ class Customers extends ApiCommand implements ResourceEntity
// now, recalculate the resource-usage for the old and the new admin // now, recalculate the resource-usage for the old and the new admin
User::updateCounters(false); User::updateCounters(false);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] moved user '" . $c_result['loginname'] . "' from admin/reseller '" . $c_result['adminname'] . " to admin/reseller '" . $a_result['loginname'] . "'");
$result = $this->apiCall('Customers.get', [ $result = $this->apiCall('Customers.get', [
'id' => $c_result['customerid'] 'id' => $c_result['customerid']

View File

@@ -144,7 +144,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$id = Database::lastInsertId(); $id = Database::lastInsertId();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-option for '" . $userpath . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-option for '" . $userpath . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$result = $this->apiCall('DirOptions.get', [ $result = $this->apiCall('DirOptions.get', [
@@ -247,7 +247,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
$params['id'] = $id; $params['id'] = $id;
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory options for '" . $result['path'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory options for '" . $result['path'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = "id #" . $id; $key = "id #" . $id;
@@ -331,7 +331,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited directory options for '" . str_replace($customer['documentroot'], '/', $result['path']) . "'");
} }
$result = $this->apiCall('DirOptions.get', [ $result = $this->apiCall('DirOptions.get', [
@@ -379,7 +379,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-options"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-options");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -478,7 +478,7 @@ class DirOptions extends ApiCommand implements ResourceEntity
"customerid" => $customer_data['customerid'], "customerid" => $customer_data['customerid'],
"id" => $id "id" => $id
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted directory-option for '" . str_replace($customer_data['documentroot'], '/', $result['path']) . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
return $this->response($result); return $this->response($result);
} }

View File

@@ -129,7 +129,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$id = Database::lastInsertId(); $id = Database::lastInsertId();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added directory-protection for '" . $username . " (" . $path . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added directory-protection for '" . $username . " (" . $path . ")'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$result = $this->apiCall('DirProtections.get', [ $result = $this->apiCall('DirProtections.get', [
@@ -196,7 +196,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
$params['idun'] = ($id <= 0 ? $username : $id); $params['idun'] = ($id <= 0 ? $username : $id);
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get directory protection for '" . $result['path'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get directory protection for '" . $result['path'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
@@ -279,7 +279,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated directory-protection '" . $result['username'] . " (" . $result['path'] . ")'");
$result = $this->apiCall('DirProtections.get', [ $result = $this->apiCall('DirProtections.get', [
'id' => $result['id'] 'id' => $result['id']
]); ]);
@@ -325,7 +325,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list directory-protections"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list directory-protections");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -413,7 +413,7 @@ class DirProtections extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]); ]);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted htpasswd for '" . $result['username'] . " (" . $result['path'] . ")'");
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
return $this->response($result); return $this->response($result);
} }

View File

@@ -413,7 +413,7 @@ class DomainZones extends ApiCommand implements ResourceEntity
$zone = Dns::createDomainZone($id); $zone = Dns::createDomainZone($id);
$zonefile = (string)$zone; $zonefile = (string)$zone;
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get dns-zone for '" . $result['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get dns-zone for '" . $result['domain'] . "'");
return $this->response(explode("\n", $zonefile)); return $this->response(explode("\n", $zonefile));
} }

View File

@@ -76,7 +76,7 @@ class Domains extends ApiCommand implements ResourceEntity
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT SELECT
`d`.*, `c`.`loginname`, `c`.`deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `c`.`adminid` as customeradmin, `d`.*, `c`.`loginname`, `c`.`deactivated` as `customer_deactivated`, `c`.`name`, `c`.`firstname`, `c`.`company`, `c`.`standardsubdomain`, `c`.`adminid` as customeradmin,
`ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain` `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`
FROM `" . TABLE_PANEL_DOMAINS . "` `d` FROM `" . TABLE_PANEL_DOMAINS . "` `d`
LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`) LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`)
@@ -110,7 +110,7 @@ class Domains extends ApiCommand implements ResourceEntity
* *
* @param number $domain_id * @param number $domain_id
* @param bool $ssl_only * @param bool $ssl_only
* optional, return only ssl enabled ip's, default false * optional, return only ssl enabled ips, default false
* @return array * @return array
*/ */
private function getIpsForDomain($domain_id = 0, $ssl_only = false) private function getIpsForDomain($domain_id = 0, $ssl_only = false)
@@ -190,9 +190,6 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, whether to create an exclusive web-logfile for this domain, default 0 (false) * optional, whether to create an exclusive web-logfile for this domain, default 0 (false)
* @param int $alias * @param int $alias
* optional, domain-id of a domain that the new domain should be an alias of, default 0 (none) * optional, domain-id of a domain that the new domain should be an alias of, default 0 (none)
* @param int $issubof
* optional, domain-id of a domain this domain is a subdomain of (required for webserver-cronjob to
* generate the correct order), default 0 (none)
* @param string $registration_date * @param string $registration_date
* optional, date of domain registration in form of YYYY-MM-DD, default empty (none) * optional, date of domain registration in form of YYYY-MM-DD, default empty (none)
* @param string $termination_date * @param string $termination_date
@@ -210,7 +207,7 @@ class Domains extends ApiCommand implements ResourceEntity
* @param string $ssl_specialsettings * @param string $ssl_specialsettings
* optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty * optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty
* @param bool $include_specialsettings * @param bool $include_specialsettings
* optional, whether or not to include non-ssl specialsettings in the generated ssl-vhost, default false * optional, whether to include non-ssl specialsettings in the generated ssl-vhost, default false
* @param bool $notryfiles * @param bool $notryfiles
* optional, [nginx only] do not generate the default try-files directive, default 0 (false) * optional, [nginx only] do not generate the default try-files directive, default 0 (false)
* @param bool $writeaccesslog * @param bool $writeaccesslog
@@ -219,7 +216,7 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, Enable writing an error-log file for this domain, default 1 (true) * optional, Enable writing an error-log file for this domain, default 1 (true)
* @param string $documentroot * @param string $documentroot
* optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be * optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be
* aware, if path starts with / it it considered a full path, not relative to customer-docroot. Also * aware, if path starts with / it is considered a full path, not relative to customer-docroot. Also
* specifying a URL is possible here (redirect), default empty (autogenerated) * specifying a URL is possible here (redirect), default empty (autogenerated)
* @param bool $phpenabled * @param bool $phpenabled
* optional, whether php is enabled for this domain, default 0 (false) * optional, whether php is enabled for this domain, default 0 (false)
@@ -244,7 +241,7 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, do NOT set the systems default ssl ip addresses if none are given via $ssl_ipandport * optional, do NOT set the systems default ssl ip addresses if none are given via $ssl_ipandport
* parameter * parameter
* @param bool $sslenabled * @param bool $sslenabled
* optional, whether or not SSL is enabled for this domain, regardless of the assigned ssl-ips, default * optional, whether SSL is enabled for this domain, regardless of the assigned ssl-ips, default
* 1 (true) * 1 (true)
* @param bool $http2 * @param bool $http2
* optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default * optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default
@@ -252,9 +249,9 @@ class Domains extends ApiCommand implements ResourceEntity
* @param int $hsts_maxage * @param int $hsts_maxage
* optional max-age value for HSTS header * optional max-age value for HSTS header
* @param bool $hsts_sub * @param bool $hsts_sub
* optional whether or not to add subdomains to the HSTS header * optional whether to add subdomains to the HSTS header
* @param bool $hsts_preload * @param bool $hsts_preload
* optional whether or not to preload HSTS header value * optional whether to preload HSTS header value
* @param bool $ocsp_stapling * @param bool $ocsp_stapling
* optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL * optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL
* @param bool $honorcipherorder * @param bool $honorcipherorder
@@ -263,7 +260,7 @@ class Domains extends ApiCommand implements ResourceEntity
* optional whether to enable or disable TLS sessiontickets (RFC 5077) for this domain. default 1 * optional whether to enable or disable TLS sessiontickets (RFC 5077) for this domain. default 1
* (true), requires SSL * (true), requires SSL
* @param bool $override_tls * @param bool $override_tls
* optional whether or not to override system-tls settings like protocol, ssl-ciphers and if applicable * optional whether to override system-tls settings like protocol, ssl-ciphers and if applicable
* tls-1.3 ciphers, requires change_serversettings flag for the admin, default false * tls-1.3 ciphers, requires change_serversettings flag for the admin, default false
* @param array $ssl_protocols * @param array $ssl_protocols
* optional list of allowed/used ssl/tls protocols, see system.ssl_protocols setting, only used/required * optional list of allowed/used ssl/tls protocols, see system.ssl_protocols setting, only used/required
@@ -298,7 +295,6 @@ class Domains extends ApiCommand implements ResourceEntity
$serveraliasoption = $this->getParam('selectserveralias', true, Settings::Get('system.domaindefaultalias')); $serveraliasoption = $this->getParam('selectserveralias', true, Settings::Get('system.domaindefaultalias'));
$speciallogfile = $this->getBoolParam('speciallogfile', true, 0); $speciallogfile = $this->getBoolParam('speciallogfile', true, 0);
$aliasdomain = intval($this->getParam('alias', true, 0)); $aliasdomain = intval($this->getParam('alias', true, 0));
$issubof = $this->getParam('issubof', true, 0);
$registration_date = $this->getParam('registration_date', true, ''); $registration_date = $this->getParam('registration_date', true, '');
$termination_date = $this->getParam('termination_date', true, ''); $termination_date = $this->getParam('termination_date', true, '');
$caneditdomain = $this->getBoolParam('caneditdomain', true, 0); $caneditdomain = $this->getBoolParam('caneditdomain', true, 0);
@@ -665,10 +661,6 @@ class Domains extends ApiCommand implements ResourceEntity
$serveraliasoption = '0'; $serveraliasoption = '0';
} }
if ($issubof <= 0) {
$issubof = '0';
}
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
if ($domain == '') { if ($domain == '') {
Response::standardError([ Response::standardError([
@@ -723,7 +715,6 @@ class Domains extends ApiCommand implements ResourceEntity
'phpsettingid' => $phpsettingid, 'phpsettingid' => $phpsettingid,
'mod_fcgid_starter' => $mod_fcgid_starter, 'mod_fcgid_starter' => $mod_fcgid_starter,
'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests, 'mod_fcgid_maxrequests' => $mod_fcgid_maxrequests,
'ismainbutsubto' => $issubof,
'letsencrypt' => $letsencrypt, 'letsencrypt' => $letsencrypt,
'http2' => $http2, 'http2' => $http2,
'hsts' => $hsts_maxage, 'hsts' => $hsts_maxage,
@@ -777,7 +768,6 @@ class Domains extends ApiCommand implements ResourceEntity
`phpsettingid` = :phpsettingid, `phpsettingid` = :phpsettingid,
`mod_fcgid_starter` = :mod_fcgid_starter, `mod_fcgid_starter` = :mod_fcgid_starter,
`mod_fcgid_maxrequests` = :mod_fcgid_maxrequests, `mod_fcgid_maxrequests` = :mod_fcgid_maxrequests,
`ismainbutsubto` = :ismainbutsubto,
`letsencrypt` = :letsencrypt, `letsencrypt` = :letsencrypt,
`http2` = :http2, `http2` = :http2,
`hsts` = :hsts, `hsts` = :hsts,
@@ -898,7 +888,7 @@ class Domains extends ApiCommand implements ResourceEntity
$result['ipsandports'] = $this->getIpsForDomain($result['id']); $result['ipsandports'] = $this->getIpsForDomain($result['id']);
} }
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']); $result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get domain '" . $result['domain'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get domain '" . $result['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
@@ -1069,9 +1059,6 @@ class Domains extends ApiCommand implements ResourceEntity
* default 0 (false) * default 0 (false)
* @param int $alias * @param int $alias
* optional, domain-id of a domain that the new domain should be an alias of, default 0 (none) * optional, domain-id of a domain that the new domain should be an alias of, default 0 (none)
* @param int $issubof
* optional, domain-id of a domain this domain is a subdomain of (required for webserver-cronjob to
* generate the correct order), default 0 (none)
* @param string $registration_date * @param string $registration_date
* optional, date of domain registration in form of YYYY-MM-DD, default empty (none) * optional, date of domain registration in form of YYYY-MM-DD, default empty (none)
* @param string $termination_date * @param string $termination_date
@@ -1089,7 +1076,7 @@ class Domains extends ApiCommand implements ResourceEntity
* @param string $ssl_specialsettings * @param string $ssl_specialsettings
* optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty * optional, custom webserver vhost-content which is added to the generated ssl-vhost, default empty
* @param bool $include_specialsettings * @param bool $include_specialsettings
* optional, whether or not to include non-ssl specialsettings in the generated ssl-vhost, default false * optional, whether to include non-ssl specialsettings in the generated ssl-vhost, default false
* @param bool $specialsettingsforsubdomains * @param bool $specialsettingsforsubdomains
* optional, whether to apply specialsettings to all subdomains of this domain, default is read from * optional, whether to apply specialsettings to all subdomains of this domain, default is read from
* setting system.apply_specialsettings_default * setting system.apply_specialsettings_default
@@ -1101,7 +1088,7 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, Enable writing an error-log file for this domain, default 1 (true) * optional, Enable writing an error-log file for this domain, default 1 (true)
* @param string $documentroot * @param string $documentroot
* optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be * optional, specify homedir of domain by specifying a directory (relative to customer-docroot), be
* aware, if path starts with / it it considered a full path, not relative to customer-docroot. Also * aware, if path starts with / it is considered a full path, not relative to customer-docroot. Also
* specifying a URL is possible here (redirect), default empty (autogenerated) * specifying a URL is possible here (redirect), default empty (autogenerated)
* @param bool $phpenabled * @param bool $phpenabled
* optional, whether php is enabled for this domain, default 0 (false) * optional, whether php is enabled for this domain, default 0 (false)
@@ -1130,7 +1117,7 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, if set to true and no $ssl_ipandport value is given, the ip's get removed, otherwise, the * optional, if set to true and no $ssl_ipandport value is given, the ip's get removed, otherwise, the
* currently set value is used, default false * currently set value is used, default false
* @param bool $sslenabled * @param bool $sslenabled
* optional, whether or not SSL is enabled for this domain, regardless of the assigned ssl-ips, default * optional, whether SSL is enabled for this domain, regardless of the assigned ssl-ips, default
* 1 (true) * 1 (true)
* @param bool $http2 * @param bool $http2
* optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default * optional, whether to enable http/2 for this domain (requires to be enabled in the settings), default
@@ -1138,9 +1125,9 @@ class Domains extends ApiCommand implements ResourceEntity
* @param int $hsts_maxage * @param int $hsts_maxage
* optional max-age value for HSTS header * optional max-age value for HSTS header
* @param bool $hsts_sub * @param bool $hsts_sub
* optional whether or not to add subdomains to the HSTS header * optional whether to add subdomains to the HSTS header
* @param bool $hsts_preload * @param bool $hsts_preload
* optional whether or not to preload HSTS header value * optional whether to preload HSTS header value
* @param bool $ocsp_stapling * @param bool $ocsp_stapling
* optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL * optional whether to enable ocsp-stapling for this domain. default 0 (false), requires SSL
* @param bool $honorcipherorder * @param bool $honorcipherorder
@@ -1150,6 +1137,8 @@ class Domains extends ApiCommand implements ResourceEntity
* (true), requires SSL * (true), requires SSL
* @param string $description * @param string $description
* optional custom description (currently not used/shown in the frontend), default empty * optional custom description (currently not used/shown in the frontend), default empty
* @param bool $deactivated
* optional, if 1 (true) the domain can be deactivated/suspended
* *
* @access admin * @access admin
* @return string json-encoded array * @return string json-encoded array
@@ -1191,7 +1180,6 @@ class Domains extends ApiCommand implements ResourceEntity
$speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']); $speciallogfile = $this->getBoolParam('speciallogfile', true, $result['speciallogfile']);
$speciallogverified = $this->getBoolParam('speciallogverified', true, 0); $speciallogverified = $this->getBoolParam('speciallogverified', true, 0);
$aliasdomain = intval($this->getParam('alias', true, $result['aliasdomain'])); $aliasdomain = intval($this->getParam('alias', true, $result['aliasdomain']));
$issubof = $this->getParam('issubof', true, $result['ismainbutsubto']);
$registration_date = $this->getParam('registration_date', true, $result['registration_date']); $registration_date = $this->getParam('registration_date', true, $result['registration_date']);
$termination_date = $this->getParam('termination_date', true, $result['termination_date']); $termination_date = $this->getParam('termination_date', true, $result['termination_date']);
$caneditdomain = $this->getBoolParam('caneditdomain', true, $result['caneditdomain']); $caneditdomain = $this->getBoolParam('caneditdomain', true, $result['caneditdomain']);
@@ -1246,6 +1234,7 @@ class Domains extends ApiCommand implements ResourceEntity
$tlsv13_cipher_list = $result['tlsv13_cipher_list']; $tlsv13_cipher_list = $result['tlsv13_cipher_list'];
} }
$description = $this->getParam('description', true, $result['description']); $description = $this->getParam('description', true, $result['description']);
$deactivated = $this->getBoolParam('deactivated', true, $result['deactivated']);
// count subdomain usage of source-domain // count subdomain usage of source-domain
$subdomains_stmt = Database::prepare(" $subdomains_stmt = Database::prepare("
@@ -1640,10 +1629,6 @@ class Domains extends ApiCommand implements ResourceEntity
Response::standardError('domainisaliasorothercustomer', '', true); Response::standardError('domainisaliasorothercustomer', '', true);
} }
if ($issubof <= 0) {
$issubof = '0';
}
if ($serveraliasoption != '1' && $serveraliasoption != '2') { if ($serveraliasoption != '1' && $serveraliasoption != '2') {
$serveraliasoption = '0'; $serveraliasoption = '0';
} }
@@ -1666,7 +1651,6 @@ class Domains extends ApiCommand implements ResourceEntity
|| $writeaccesslog != $result['writeaccesslog'] || $writeaccesslog != $result['writeaccesslog']
|| $writeerrorlog != $result['writeerrorlog'] || $writeerrorlog != $result['writeerrorlog']
|| $aliasdomain != $result['aliasdomain'] || $aliasdomain != $result['aliasdomain']
|| $issubof != $result['ismainbutsubto']
|| $email_only != $result['email_only'] || $email_only != $result['email_only']
|| ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1') || ($speciallogfile != $result['speciallogfile'] && $speciallogverified == '1')
|| $letsencrypt != $result['letsencrypt'] || $letsencrypt != $result['letsencrypt']
@@ -1801,7 +1785,7 @@ class Domains extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, [ Database::pexecute($upd_stmt, [
'id' => $id 'id' => $id
], true, true); ], true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] removed specialsettings on all subdomains of domain #" . $id); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] removed specialsettings on all subdomains of domain #" . $id);
} }
$wwwserveralias = ($serveraliasoption == '1') ? '1' : '0'; $wwwserveralias = ($serveraliasoption == '1') ? '1' : '0';
@@ -1837,7 +1821,6 @@ class Domains extends ApiCommand implements ResourceEntity
$update_data['writeerrorlog'] = $writeerrorlog; $update_data['writeerrorlog'] = $writeerrorlog;
$update_data['registration_date'] = $registration_date; $update_data['registration_date'] = $registration_date;
$update_data['termination_date'] = $termination_date; $update_data['termination_date'] = $termination_date;
$update_data['ismainbutsubto'] = $issubof;
$update_data['letsencrypt'] = $letsencrypt; $update_data['letsencrypt'] = $letsencrypt;
$update_data['http2'] = $http2; $update_data['http2'] = $http2;
$update_data['hsts'] = $hsts_maxage; $update_data['hsts'] = $hsts_maxage;
@@ -1852,6 +1835,7 @@ class Domains extends ApiCommand implements ResourceEntity
$update_data['honorcipherorder'] = $honorcipherorder; $update_data['honorcipherorder'] = $honorcipherorder;
$update_data['sessiontickets'] = $sessiontickets; $update_data['sessiontickets'] = $sessiontickets;
$update_data['description'] = $description; $update_data['description'] = $description;
$update_data['deactivated'] = $deactivated;
$update_data['id'] = $id; $update_data['id'] = $id;
$update_stmt = Database::prepare(" $update_stmt = Database::prepare("
@@ -1885,7 +1869,6 @@ class Domains extends ApiCommand implements ResourceEntity
`writeerrorlog` = :writeerrorlog, `writeerrorlog` = :writeerrorlog,
`registration_date` = :registration_date, `registration_date` = :registration_date,
`termination_date` = :termination_date, `termination_date` = :termination_date,
`ismainbutsubto` = :ismainbutsubto,
`letsencrypt` = :letsencrypt, `letsencrypt` = :letsencrypt,
`http2` = :http2, `http2` = :http2,
`hsts` = :hsts, `hsts` = :hsts,
@@ -1899,11 +1882,36 @@ class Domains extends ApiCommand implements ResourceEntity
`ssl_enabled` = :sslenabled, `ssl_enabled` = :sslenabled,
`ssl_honorcipherorder` = :honorcipherorder, `ssl_honorcipherorder` = :honorcipherorder,
`ssl_sessiontickets` = :sessiontickets, `ssl_sessiontickets` = :sessiontickets,
`description` = :description `description` = :description,
`deactivated` = :deactivated
WHERE `id` = :id WHERE `id` = :id
"); ");
Database::pexecute($update_stmt, $update_data, true, true); Database::pexecute($update_stmt, $update_data, true, true);
// activate/deactivate domain-based services
if ($deactivated != $result['deactivated']) {
// deactivate email accounts
$yesno = ($deactivated ? 'N' : 'Y');
$pop3 = ($deactivated ? '0' : (int)$customer['pop3']);
$imap = ($deactivated ? '0' : (int)$customer['imap']);
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_MAIL_USERS . "`
SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap
WHERE `customerid` = :customerid AND `domainid` = :domainid
");
Database::pexecute($upd_stmt, [
'yesno' => $yesno,
'pop3' => $pop3,
'imap' => $imap,
'customerid' => $customerid,
'domainid' => $id
]);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " domain '" . $result['domain'] . "'");
Cronjob::inserttask(TaskId::REBUILD_VHOST);
}
$_update_data['customerid'] = $customerid; $_update_data['customerid'] = $customerid;
$_update_data['adminid'] = $adminid; $_update_data['adminid'] = $adminid;
$_update_data['phpenabled'] = $phpenabled; $_update_data['phpenabled'] = $phpenabled;
@@ -1921,6 +1929,7 @@ class Domains extends ApiCommand implements ResourceEntity
$_update_data['honorcipherorder'] = $honorcipherorder; $_update_data['honorcipherorder'] = $honorcipherorder;
$_update_data['sessiontickets'] = $sessiontickets; $_update_data['sessiontickets'] = $sessiontickets;
$_update_data['parentdomainid'] = $id; $_update_data['parentdomainid'] = $id;
$_update_data['deactivated'] = $deactivated;
// if php config is to be set for all subdomains, check here // if php config is to be set for all subdomains, check here
$update_phpconfig = ''; $update_phpconfig = '';
@@ -1953,7 +1962,8 @@ class Domains extends ApiCommand implements ResourceEntity
`ssl_cipher_list` = :ssl_cipher_list, `ssl_cipher_list` = :ssl_cipher_list,
`tlsv13_cipher_list` = :tlsv13_cipher_list, `tlsv13_cipher_list` = :tlsv13_cipher_list,
`ssl_honorcipherorder` = :honorcipherorder, `ssl_honorcipherorder` = :honorcipherorder,
`ssl_sessiontickets` = :sessiontickets `ssl_sessiontickets` = :sessiontickets,
`deactivated` = :deactivated
" . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . " " . $update_phpconfig . $upd_specialsettings . $updatechildren . $update_sslredirect . "
WHERE `parentdomainid` = :parentdomainid WHERE `parentdomainid` = :parentdomainid
"); ");
@@ -2073,9 +2083,6 @@ class Domains extends ApiCommand implements ResourceEntity
* optional, the domain-id * optional, the domain-id
* @param string $domainname * @param string $domainname
* optional, the domainname * optional, the domainname
* @param bool $delete_mainsubdomains
* optional, remove also domains that are subdomains of this domain but added as main domains; default
* false
* @param bool $is_stdsubdomain * @param bool $is_stdsubdomain
* optional, default false, specify whether it's a std-subdomain you are deleting as it does not count * optional, default false, specify whether it's a std-subdomain you are deleting as it does not count
* as subdomain-resource * as subdomain-resource
@@ -2091,7 +2098,6 @@ class Domains extends ApiCommand implements ResourceEntity
$dn_optional = $id > 0; $dn_optional = $id > 0;
$domainname = $this->getParam('domainname', $dn_optional, ''); $domainname = $this->getParam('domainname', $dn_optional, '');
$is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0); $is_stdsubdomain = $this->getParam('is_stdsubdomain', true, 0);
$remove_subbutmain_domains = $this->getParam('delete_mainsubdomains', true, 0);
$result = $this->apiCall('Domains.get', [ $result = $this->apiCall('Domains.get', [
'id' => $id, 'id' => $id,
@@ -2099,15 +2105,10 @@ class Domains extends ApiCommand implements ResourceEntity
]); ]);
$id = $result['id']; $id = $result['id'];
// check for deletion of main-domains which are logically subdomains, #329
$rsd_sql = '';
if ($remove_subbutmain_domains) {
$rsd_sql .= " OR `ismainbutsubto` = :id";
}
$subresult_stmt = Database::prepare(" $subresult_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE (`id` = :id OR `parentdomainid` = :id " . $rsd_sql . ")"); WHERE (`id` = :id OR `parentdomainid` = :id)
");
Database::pexecute($subresult_stmt, [ Database::pexecute($subresult_stmt, [
'id' => $id 'id' => $id
], true, true); ], true, true);
@@ -2129,23 +2130,10 @@ class Domains extends ApiCommand implements ResourceEntity
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] deleted domain/s from mail-tables"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] deleted domain/s from mail-tables");
} }
// if mainbutsubto-domains are not to be deleted, re-assign the (ismainbutsubto value of the main
// domain which is being deleted) as their new ismainbutsubto value
if ($remove_subbutmain_domains !== 1) {
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_DOMAINS . "` SET
`ismainbutsubto` = :newIsMainButSubtoValue
WHERE `ismainbutsubto` = :deletedMainDomainId
");
Database::pexecute($upd_stmt, [
'newIsMainButSubtoValue' => $result['ismainbutsubto'],
'deletedMainDomainId' => $id
], true, true);
}
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_PANEL_DOMAINS . "` DELETE FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `id` = :id OR `parentdomainid` = :id " . $rsd_sql); WHERE `id` = :id OR `parentdomainid` = :id
");
Database::pexecute($del_stmt, [ Database::pexecute($del_stmt, [
'id' => $id 'id' => $id
], true, true); ], true, true);
@@ -2221,7 +2209,7 @@ class Domains extends ApiCommand implements ResourceEntity
// remove domain from acme.sh / lets encrypt if used // remove domain from acme.sh / lets encrypt if used
Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']); Cronjob::inserttask(TaskId::DELETE_DOMAIN_SSL, $result['domain']);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] deleted domain/subdomains (#" . $result['id'] . ")"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] deleted domain/subdomains (#" . $result['id'] . ")");
User::updateCounters(); User::updateCounters();
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
// Using nameserver, insert a task which rebuilds the server config // Using nameserver, insert a task which rebuilds the server config
@@ -2230,4 +2218,114 @@ class Domains extends ApiCommand implements ResourceEntity
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);
} }
/**
* duplicate domain entry by either id or domainname. All parameters from Domains.add() can be used
* to overwrite source entity values if necessary.
*
* @param int $id
* optional, the domain-id
* @param string $domainname
* optional, the domainname
* @param string $domain
* required, name of the new domain to be added
*
* @access admin
* @return string json-encoded array
* @throws Exception
*/
public function duplicate()
{
if ($this->isAdmin()) {
// parameters
$id = $this->getParam('id', true, 0);
$dn_optional = $id > 0;
$domainname = $this->getParam('domainname', $dn_optional, '');
$p_domain = $this->getParam('domain');
// get requested domain
$result = $this->apiCall('Domains.get', [
'id' => $id,
'domainname' => $domainname,
]);
// clear some defaults
unset($result['domain_ace']);
unset($result['adminid']);
unset($result['documentroot']);
unset($result['registration_date']);
unset($result['termination_date']);
unset($result['zonefile']);
// clear auto-generated values
unset($result['bindserial']);
unset($result['dkim_privkey']);
unset($result['dkim_pubkey']);
// clear api-call generated fields
unset($result['domain_hascert']);
// set correct ip/port information
$domain_ips = $result['ipsandports'];
unset($result['ipsandports']);
$result['ipandport'] = [];
$result['ssl_ipandport'] = [];
foreach ($domain_ips as $dip) {
if ($dip['ssl'] == 1) {
$result['ssl_ipandport'][] = $dip['id'];
} else {
$result['ipandport'][] = $dip['id'];
}
}
// check whether we are changing the customer/owner
if ($this->getParam('customerid', true, 0) == 0 && $this->getParam('loginname', true, '') == '') {
$customerid = $result['customerid'];
} else {
$customer = $this->getCustomerData();
$customerid = $customer['customerid'];
}
// check for alias-domain and whether it belongs to the target user
if (!empty($result['aliasdomain']) && $customerid == $result['customerid']) {
// duplicate alias entry
$result['alias'] = $result['aliasdomain'];
}
unset($result['aliasdomain']);
// validate possible fpm configs and whether the customer is allowed to use them
if ($customerid != $result['customerid']) {
$allowed_phpconfigs = json_decode($customer['allowed_phpconfigs'] ?? '[]', true);
if (empty($allowed_phpconfigs)) {
// system defaults
unset($result['phpsettingid']);
} elseif (!in_array($result['phpsettingid'], $allowed_phpconfigs)) {
// use the first customer allowed config
$result['phpsettingid'] = array_shift($allowed_phpconfigs);
}
}
// translate serveralias values
$result['selectserveralias'] = 2;
if ((int)$result['wwwserveralias'] == 1) {
$result['selectserveralias'] = 1;
} elseif ((int)$result['iswildcarddomain'] == 1) {
$result['selectserveralias'] = 0;
}
unset($result['wwwserveralias']);
unset($result['iswildcarddomain']);
$additional_params = $this->getParamList();
// unset unneeded params from this call
unset($additional_params['id']);
unset($additional_params['domainname']);
unset($additional_params['domain']);
// set new values and merge with optional add() parameters
$new_domain = array_merge($result, $additional_params);
$new_domain['domain'] = $p_domain;
$result_new = $this->apiCall('Domains.add', $new_domain);
return $this->response($result_new);
}
throw new Exception("Not allowed to execute given command.", 403);
}
} }

View File

@@ -95,9 +95,18 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$customer = $this->getCustomerData('email_accounts'); $customer = $this->getCustomerData('email_accounts');
// check for imap||pop3 == 1, see #1298 // check for imap||pop3 == 1, see #1298
// d00p, 6.5.2023 @revert this - if a customer has resources which allow email accounts
// it implicitly allowed SMTP, e.g. sending of emails which also requires an account to exist
/*
if ($customer['imap'] != '1' && $customer['pop3'] != '1') { if ($customer['imap'] != '1' && $customer['pop3'] != '1') {
Response::standardError('notallowedtouseaccounts', '', true); Response::standardError('notallowedtouseaccounts', '', true);
} }
*/
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// get email address // get email address
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
@@ -306,7 +315,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
} }
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email account for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email account for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
]); ]);
@@ -357,6 +366,11 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
$ea_optional = $id > 0; $ea_optional = $id > 0;
$emailaddr = $this->getParam('emailaddr', $ea_optional, ''); $emailaddr = $this->getParam('emailaddr', $ea_optional, '');
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
// validation // validation
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
@@ -450,7 +464,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota'])); Admins::increaseUsage($customer['adminid'], 'email_quota_used', '', ($quota - $result['quota']));
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated email account '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated email account '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
]); ]);
@@ -556,7 +570,7 @@ class EmailAccounts extends ApiCommand implements ResourceEntity
Customers::decreaseUsage($customer['customerid'], 'email_accounts_used'); Customers::decreaseUsage($customer['customerid'], 'email_accounts_used');
Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota); Customers::decreaseUsage($customer['customerid'], 'email_quota_used', '', $quota);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email account for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email account for '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
} }

View File

@@ -89,7 +89,7 @@ class EmailDomains extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO,
"[API] list email-domains"); "[API] list email-domains");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),

View File

@@ -77,6 +77,11 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$destination = $idna_convert->encode($destination); $destination = $idna_convert->encode($destination);
if (!empty($emailaddr)) {
$idna_convert = new IdnaWrapper();
$emailaddr = $idna_convert->encode($emailaddr);
}
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'id' => $id, 'id' => $id,
'emailaddr' => $emailaddr 'emailaddr' => $emailaddr
@@ -116,7 +121,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::increaseUsage($customer['customerid'], 'email_forwarders_used'); Customers::increaseUsage($customer['customerid'], 'email_forwarders_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email forwarder for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email forwarder for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
@@ -293,7 +298,7 @@ class EmailForwarders extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used'); Customers::decreaseUsage($customer['customerid'], 'email_forwarders_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email forwarder for '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] deleted email forwarder for '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']

View File

@@ -88,9 +88,12 @@ class Emails extends ApiCommand implements ResourceEntity
$domain_check = $this->apiCall('SubDomains.get', [ $domain_check = $this->apiCall('SubDomains.get', [
'domainname' => $domain 'domainname' => $domain
], true); ], true);
if ($domain_check['isemaildomain'] == 0) { if ((int)$domain_check['isemaildomain'] == 0) {
Response::standardError('maindomainnonexist', $domain, true); Response::standardError('maindomainnonexist', $domain, true);
} }
if ((int)$domain_check['deactivated'] == 1) {
Response::standardError('maindomaindeactivated', $domain, true);
}
if (Settings::Get('catchall.catchall_enabled') != '1') { if (Settings::Get('catchall.catchall_enabled') != '1') {
$iscatchall = 0; $iscatchall = 0;
@@ -159,7 +162,7 @@ class Emails extends ApiCommand implements ResourceEntity
// update customer usage // update customer usage
Customers::increaseUsage($customer['customerid'], 'emails_used'); Customers::increaseUsage($customer['customerid'], 'emails_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added email address '" . $email_full . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added email address '" . $email_full . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $email_full 'emailaddr' => $email_full
@@ -199,7 +202,7 @@ class Emails extends ApiCommand implements ResourceEntity
); );
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get email address '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'"); $key = ($id > 0 ? "id #" . $id : "emailaddr '" . $emailaddr . "'");
@@ -294,7 +297,7 @@ class Emails extends ApiCommand implements ResourceEntity
"id" => $id "id" => $id
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled catchall-flag for email address '" . $result['email_full'] . "'");
$result = $this->apiCall('Emails.get', [ $result = $this->apiCall('Emails.get', [
'emailaddr' => $result['email_full'] 'emailaddr' => $result['email_full']
@@ -340,7 +343,7 @@ class Emails extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list email-addresses"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list email-addresses");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result
@@ -445,7 +448,7 @@ class Emails extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Customers::decreaseUsage($customer['customerid'], 'emails_used'); Customers::decreaseUsage($customer['customerid'], 'emails_used');
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] deleted email address '" . $result['email_full'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] deleted email address '" . $result['email_full'] . "'");
return $this->response($result); return $this->response($result);
} }
} }

View File

@@ -64,7 +64,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list fpm-daemons"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list fpm-daemons");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit()); SELECT * FROM `" . TABLE_PANEL_FPMDAEMONS . "`" . $this->getSearchWhere($query_fields) . $this->getOrderBy() . $this->getLimit());
@@ -258,7 +258,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
$id = Database::lastInsertId(); $id = Database::lastInsertId();
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('FpmDaemons.get', [ $result = $this->apiCall('FpmDaemons.get', [
'id' => $id 'id' => $id
]); ]);
@@ -384,7 +384,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('FpmDaemons.get', [ $result = $this->apiCall('FpmDaemons.get', [
'id' => $id 'id' => $id
]); ]);
@@ -433,7 +433,7 @@ class FpmDaemons extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] fpm-daemon setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -37,6 +37,7 @@ use Froxlor\Settings;
use Froxlor\SImExporter; use Froxlor\SImExporter;
use Froxlor\System\Cronjob; use Froxlor\System\Cronjob;
use Froxlor\System\Crypt; use Froxlor\System\Crypt;
use Froxlor\Validate\Validate;
use PDO; use PDO;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
@@ -72,7 +73,7 @@ class Froxlor extends ApiCommand
if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) { if (empty($uc_data) || empty($response) || $uc_data['ts'] + self::UPDATE_CHECK_INTERVAL < time() || $uc_data['channel'] != Settings::Get('system.update_channel') || $force_ucheck) {
// log our actions // log our actions
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] checking for updates"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] checking for updates");
// check for new version // check for new version
$aucheck = AutoUpdate::checkVersion(); $aucheck = AutoUpdate::checkVersion();
@@ -142,7 +143,7 @@ class Froxlor extends ApiCommand
{ {
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$json_str = $this->getParam('json_str'); $json_str = $this->getParam('json_str');
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "User " . $this->getUserDetail('loginname') . " imported settings"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "User " . $this->getUserDetail('loginname') . " imported settings");
try { try {
SImExporter::import($json_str); SImExporter::import($json_str);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
@@ -269,6 +270,79 @@ class Froxlor extends ApiCommand
return $this->response(Crypt::generatePassword($length)); return $this->response(Crypt::generatePassword($length));
} }
/**
* return a one-time login link URL for a given user
*
* @param int $customerid optional, required if $loginname is not specified, user to create link for
* @param string $loginname optional, required if $customerid is not specified, user to create link for
* @param int $valid_time optional, value in seconds how long the link will be valid, default is 10 seconds, valid values are numbers from 10 to 120
* @param string $allowed_from optional, comma separated list of ip addresses or networks to allow login from via this link
*
* @access admin
* @return string json-encoded array [base => domain, uri => relative link]
* @throws Exception
*/
public function generateLoginLink()
{
if ($this->isAdmin()) {
$customer = $this->getCustomerData();
// cannot create link for deactivated users
if ((int)$customer['deactivated'] == 1) {
throw new Exception("Cannot generate link for deactivated user", 406);
}
$valid_time = (int)$this->getParam('valid_time', true, 10);
$allowed_from = $this->getParam('allowed_from', true, '');
$valid_time = Validate::validate($valid_time, 'valid time', '/^(1[0-1][0-9]|120|[1-9][0-9])$/', 'invalid_validtime', [10], true);
// 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) {
throw new Exception('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));
}
$hash = hash('sha256', openssl_random_pseudo_bytes(64 * 64));
$ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_PANEL_LOGINLINKS . "`
SET `hash` = :hash, `loginname` = :loginname, `valid_until` = :validuntil, `allowed_from` = :allowedfrom
ON DUPLICATE KEY UPDATE `hash` = :hash, `valid_until` = :validuntil, `allowed_from` = :allowedfrom
");
Database::pexecute($ins_stmt, [
'hash' => $hash,
'loginname' => $customer['loginname'],
'validuntil' => time() + $valid_time,
'allowedfrom' => $allowed_from
]);
return $this->response([
'base' => 'https://' . Settings::Get('system.hostname') . '/' . (Settings::Get('system.froxlordirectlyviahostname') != 1 ? basename(\Froxlor\Froxlor::getInstallDir()) . '/' : ''),
'uri' => 'index.php?action=ll&ln=' . $customer['loginname'] . '&h=' . $hash
]);
}
throw new Exception("Not allowed to execute given command.", 403);
}
/** /**
* can be used to remotely run the integritiy checks froxlor implements * can be used to remotely run the integritiy checks froxlor implements
* *

View File

@@ -257,7 +257,7 @@ class Ftps extends ApiCommand implements ResourceEntity
Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber'); Customers::increaseUsage($customer['customerid'], 'ftp_lastaccountnumber');
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] added ftp-account '" . $username . " (" . $path . ")'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-account '" . $username . " (" . $path . ")'");
Cronjob::inserttask(TaskId::CREATE_FTP); Cronjob::inserttask(TaskId::CREATE_FTP);
if ($sendinfomail == 1) { if ($sendinfomail == 1) {
@@ -302,7 +302,7 @@ class Ftps extends ApiCommand implements ResourceEntity
$this->mailer()->clearAddresses(); $this->mailer()->clearAddresses();
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added ftp-user '" . $username . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added ftp-user '" . $username . "'");
$result = $this->apiCall('Ftps.get', [ $result = $this->apiCall('Ftps.get', [
'username' => $username 'username' => $username
@@ -367,7 +367,7 @@ class Ftps extends ApiCommand implements ResourceEntity
$params['idun'] = ($id <= 0 ? $username : $id); $params['idun'] = ($id <= 0 ? $username : $id);
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get ftp-user '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get ftp-user '" . $result['username'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "username '" . $username . "'"); $key = ($id > 0 ? "id #" . $id : "username '" . $username . "'");
@@ -453,7 +453,7 @@ class Ftps extends ApiCommand implements ResourceEntity
"id" => $id, "id" => $id,
"password" => $cryptPassword "password" => $cryptPassword
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account password for '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account password for '" . $result['username'] . "'");
} }
// path update? // path update?
@@ -471,7 +471,7 @@ class Ftps extends ApiCommand implements ResourceEntity
"customerid" => $customer['customerid'], "customerid" => $customer['customerid'],
"id" => $id "id" => $id
], true, true); ], true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] updated ftp-account homdir for '" . $result['username'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated ftp-account homdir for '" . $result['username'] . "'");
} }
} }
// it's the task for "new ftp" but that will // it's the task for "new ftp" but that will
@@ -533,7 +533,7 @@ class Ftps extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list ftp-users"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list ftp-users");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -61,7 +61,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list hosting-plans"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list hosting-plans");
$query_fields = []; $query_fields = [];
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT p.*, a.loginname as adminname SELECT p.*, a.loginname as adminname
@@ -227,7 +227,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
'valuearr' => json_encode($value_arr) 'valuearr' => json_encode($value_arr)
]; ];
Database::pexecute($ins_stmt, $ins_data, true, true); Database::pexecute($ins_stmt, $ins_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added hosting-plan '" . $name . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] added hosting-plan '" . $name . "'");
$result = $this->apiCall('HostingPlans.get', [ $result = $this->apiCall('HostingPlans.get', [
'planname' => $name 'planname' => $name
]); ]);
@@ -264,7 +264,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
} }
$result = Database::pexecute_first($result_stmt, $params, true, true); $result = Database::pexecute_first($result_stmt, $params, true, true);
if ($result) { if ($result) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get hosting-plan '" . $result['name'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get hosting-plan '" . $result['name'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'"); $key = ($id > 0 ? "id #" . $id : "planname '" . $planname . "'");
@@ -414,7 +414,7 @@ class HostingPlans extends ApiCommand implements ResourceEntity
'id' => $id 'id' => $id
]; ];
Database::pexecute($upd_stmt, $update_data, true, true); Database::pexecute($upd_stmt, $update_data, true, true);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] updated hosting-plan '" . $result['name'] . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] updated hosting-plan '" . $result['name'] . "'");
return $this->response($update_data); return $this->response($update_data);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -65,7 +65,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) { if ($this->isAdmin() && ($this->getUserDetail('change_serversettings') || !empty($this->getUserDetail('ip')))) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list ips and ports"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list ips and ports");
$ip_where = ""; $ip_where = "";
$append_where = false; $append_where = false;
if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) { if (!empty($this->getUserDetail('ip')) && $this->getUserDetail('ip') != -1) {
@@ -335,7 +335,7 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
'id' => $id 'id' => $id
], true, true); ], true, true);
if ($result) { if ($result) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] get ip " . $result['ip'] . " " . $result['port']); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get ip " . $result['ip'] . " " . $result['port']);
return $this->response($result); return $this->response($result);
} }
throw new Exception("IP/port with id #" . $id . " could not be found", 404); throw new Exception("IP/port with id #" . $id . " could not be found", 404);

View File

@@ -26,14 +26,15 @@
namespace Froxlor\Api\Commands; namespace Froxlor\Api\Commands;
use Exception; use Exception;
use PDO;
use PDOException;
use Froxlor\Froxlor;
use Froxlor\PhpHelper;
use Froxlor\Api\ApiCommand; use Froxlor\Api\ApiCommand;
use Froxlor\Api\ResourceEntity; use Froxlor\Api\ResourceEntity;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Froxlor;
use Froxlor\FroxlorLogger;
use Froxlor\PhpHelper;
use Froxlor\Validate\Validate; use Froxlor\Validate\Validate;
use PDO;
use PDOException;
class MysqlServer extends ApiCommand implements ResourceEntity class MysqlServer extends ApiCommand implements ResourceEntity
{ {
@@ -73,8 +74,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional, test connection with given credentials, default is true (yes) * optional, test connection with given credentials, default is true (yes)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function add() public function add()
{ {
@@ -112,7 +113,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
); );
if (!empty($mysql_ca)) { if (!empty($mysql_ca)) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca; $options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert; $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
} }
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";"; $dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
@@ -167,6 +168,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
$this->addDatabaseFromCustomerAllowedList($newdbserver); $this->addDatabaseFromCustomerAllowedList($newdbserver);
} }
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] added new database server '" . $description . "' (" . $mysql_host . ")");
return $this->response(['dbserver' => $newdbserver]); return $this->response(['dbserver' => $newdbserver]);
} }
@@ -179,16 +182,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional the number of the mysql server (either id or dbserver must be set) * optional the number of the mysql server (either id or dbserver must be set)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function delete() public function delete()
{ {
$this->validateAccess(); $this->validateAccess();
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
if ($dbserver == 0) { if ($dbserver == 0) {
@@ -212,8 +215,12 @@ class MysqlServer extends ApiCommand implements ResourceEntity
// when removing, remove from list of allowed_mysqlservers from any customers // when removing, remove from list of allowed_mysqlservers from any customers
$this->removeDatabaseFromCustomerAllowedList($dbserver); $this->removeDatabaseFromCustomerAllowedList($dbserver);
$description = $sql_root[$dbserver]['caption'] ?? "unknown";
$mysql_host = $sql_root[$dbserver]['host'] ?? "unknown";
unset($sql_root[$dbserver]); unset($sql_root[$dbserver]);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] removed database server '" . $description . "' (" . $mysql_host . ")");
$this->generateNewUserData($sql, $sql_root); $this->generateNewUserData($sql, $sql_root);
return $this->response(['true']); return $this->response(['true']);
} }
@@ -287,14 +294,14 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional the number of the mysql server (either id or dbserver must be set) * optional the number of the mysql server (either id or dbserver must be set)
* *
* @access admin, customer * @access admin, customer
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function get() public function get()
{ {
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
$sql_root = []; $sql_root = [];
@@ -317,6 +324,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
unset($sql_root[$dbserver]['password']); unset($sql_root[$dbserver]['password']);
$sql_root[$dbserver]['id'] = $dbserver; $sql_root[$dbserver]['id'] = $dbserver;
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] get database-server '" . $sql_root[$dbserver]['caption'] . "'");
return $this->response($sql_root[$dbserver]); return $this->response($sql_root[$dbserver]);
} }
@@ -347,16 +355,16 @@ class MysqlServer extends ApiCommand implements ResourceEntity
* optional, test connection with given credentials, default is true (yes) * optional, test connection with given credentials, default is true (yes)
* *
* @access admin * @access admin
* @throws Exception
* @return string json-encoded array * @return string json-encoded array
* @throws Exception
*/ */
public function update() public function update()
{ {
$this->validateAccess(); $this->validateAccess();
$id = (int) $this->getParam('id', true, -1); $id = (int)$this->getParam('id', true, -1);
$dn_optional = $id >= 0; $dn_optional = $id >= 0;
$dbserver = (int) $this->getParam('dbserver', $dn_optional, -1); $dbserver = (int)$this->getParam('dbserver', $dn_optional, -1);
$dbserver = $id >= 0 ? $id : $dbserver; $dbserver = $id >= 0 ? $id : $dbserver;
$sql_root = []; $sql_root = [];
@@ -417,7 +425,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
); );
if (!empty($mysql_ca)) { if (!empty($mysql_ca)) {
$options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca; $options[PDO::MYSQL_ATTR_SSL_CA] = $mysql_ca;
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool) $mysql_verifycert; $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$mysql_verifycert;
} }
$dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";"; $dsn = "mysql:host=" . $mysql_host . ";port=" . $mysql_port . ";";
@@ -448,6 +456,8 @@ class MysqlServer extends ApiCommand implements ResourceEntity
$this->addDatabaseFromCustomerAllowedList($dbserver); $this->addDatabaseFromCustomerAllowedList($dbserver);
} }
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] edited database server '" . $description . "' (" . $mysql_host . ")");
return $this->response(['true']); return $this->response(['true']);
} }
@@ -472,7 +482,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity
WHERE `dbserver` = :dbserver WHERE `dbserver` = :dbserver
"); ");
$result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true); $result = Database::pexecute_first($result_stmt, ['dbserver' => $dbserver], true, true);
return (int) $result['num_dbs']; return (int)$result['num_dbs'];
} else { } else {
$dbserver = $this->getParam('mysql_server'); $dbserver = $this->getParam('mysql_server');
$customer_ids = $this->getAllowedCustomerIds(); $customer_ids = $this->getAllowedCustomerIds();

View File

@@ -199,7 +199,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
$this->mailer()->clearAddresses(); $this->mailer()->clearAddresses();
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] added mysql-database '" . $username . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] added mysql-database '" . $username . "'");
$result = $this->apiCall('Mysqls.get', [ $result = $this->apiCall('Mysqls.get', [
'dbname' => $username, 'dbname' => $username,
@@ -299,7 +299,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
$mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC); $mbdata = $mbdata_stmt->fetch(PDO::FETCH_ASSOC);
Database::needRoot(false); Database::needRoot(false);
$result['size'] = $mbdata['MB'] ?? 0; $result['size'] = $mbdata['MB'] ?? 0;
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get database '" . $result['databasename'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get database '" . $result['databasename'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'"); $key = ($id > 0 ? "id #" . $id : "dbname '" . $dbname . "'");
@@ -388,7 +388,7 @@ class Mysqls extends ApiCommand implements ResourceEntity
]; ];
Database::pexecute($stmt, $params, true, true); Database::pexecute($stmt, $params, true, true);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_WARNING, "[API] updated mysql-database '" . $result['databasename'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] updated mysql-database '" . $result['databasename'] . "'");
$result = $this->apiCall('Mysqls.get', [ $result = $this->apiCall('Mysqls.get', [
'dbname' => $result['databasename'] 'dbname' => $result['databasename']
]); ]);

View File

@@ -67,7 +67,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
public function listing() public function listing()
{ {
if ($this->isAdmin()) { if ($this->isAdmin()) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] list php-configs"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list php-configs");
$with_subdomains = $this->getBoolParam('with_subdomains', true, false); $with_subdomains = $this->getBoolParam('with_subdomains', true, false);
$query_fields = []; $query_fields = [];
@@ -392,7 +392,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
$ins_data['id'] = Database::lastInsertId(); $ins_data['id'] = Database::lastInsertId();
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been created by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('PhpSettings.get', [ $result = $this->apiCall('PhpSettings.get', [
'id' => $ins_data['id'] 'id' => $ins_data['id']
@@ -629,7 +629,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
Database::pexecute($upd_stmt, $upd_data, true, true); Database::pexecute($upd_stmt, $upd_data, true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_NOTICE, "[API] php setting with description '" . $description . "' has been updated by '" . $this->getUserDetail('loginname') . "'");
$result = $this->apiCall('PhpSettings.get', [ $result = $this->apiCall('PhpSettings.get', [
'id' => $id 'id' => $id
@@ -686,7 +686,7 @@ class PhpSettings extends ApiCommand implements ResourceEntity
], true, true); ], true, true);
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'"); $this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_WARNING, "[API] php setting '" . $result['description'] . "' has been deleted by '" . $this->getUserDetail('loginname') . "'");
return $this->response($result); return $this->response($result);
} }
throw new Exception("Not allowed to execute given command.", 403); throw new Exception("Not allowed to execute given command.", 403);

View File

@@ -229,6 +229,9 @@ class SubDomains extends ApiCommand implements ResourceEntity
} elseif ($completedomain_check && strtolower($completedomain_check['domain']) == strtolower($completedomain)) { } elseif ($completedomain_check && strtolower($completedomain_check['domain']) == strtolower($completedomain)) {
// the domain does already exist as main-domain // the domain does already exist as main-domain
Response::standardError('domainexistalready', $completedomain, true); Response::standardError('domainexistalready', $completedomain, true);
} elseif ((int)$domain_check['deactivated'] == 1) {
// main domain is deactivated
Response::standardError('maindomaindeactivated', $domain, true);
} }
// if allowed, check for 'is email domain'-flag // if allowed, check for 'is email domain'-flag
@@ -486,7 +489,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
$result['ipsandports'] = $this->getIpsForDomain($result['id']); $result['ipsandports'] = $this->getIpsForDomain($result['id']);
} }
$result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']); $result['domain_hascert'] = $this->getHasCertValueForDomain((int)$result['id'], (int)$result['parentdomainid']);
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] get subdomain '" . $result['domain'] . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] get subdomain '" . $result['domain'] . "'");
return $this->response($result); return $this->response($result);
} }
$key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'"); $key = ($id > 0 ? "id #" . $id : "domainname '" . $domainname . "'");
@@ -856,7 +859,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
Cronjob::inserttask(TaskId::REBUILD_VHOST); Cronjob::inserttask(TaskId::REBUILD_VHOST);
Cronjob::inserttask(TaskId::REBUILD_DNS); Cronjob::inserttask(TaskId::REBUILD_DNS);
$idna_convert = new IdnaWrapper(); $idna_convert = new IdnaWrapper();
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] edited domain '" . $idna_convert->decode($result['domain']) . "'");
} }
$result = $this->apiCall('SubDomains.get', [ $result = $this->apiCall('SubDomains.get', [
'id' => $id 'id' => $id
@@ -865,7 +868,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
} }
/** /**
* lists all subdomain entries * lists all customer domain/subdomain entries
* *
* @param bool $with_ips * @param bool $with_ips
* optional, default true * optional, default true
@@ -910,17 +913,12 @@ class SubDomains extends ApiCommand implements ResourceEntity
$custom_list_result = $_custom_list_result['list']; $custom_list_result = $_custom_list_result['list'];
} }
$customer_ids = []; $customer_ids = [];
$customer_stdsubs = [];
foreach ($custom_list_result as $customer) { foreach ($custom_list_result as $customer) {
$customer_ids[] = $customer['customerid']; $customer_ids[] = $customer['customerid'];
$customer_stdsubs[$customer['customerid']] = $customer['standardsubdomain'];
} }
if (empty($customer_ids)) { if (empty($customer_ids)) {
throw new Exception("Required resource unsatisfied.", 405); throw new Exception("Required resource unsatisfied.", 405);
} }
if (empty($customer_stdsubs)) {
throw new Exception("Required resource unsatisfied.", 405);
}
$select_fields = [ $select_fields = [
'`d`.*' '`d`.*'
@@ -932,9 +930,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
$customer_ids = [ $customer_ids = [
$this->getUserDetail('customerid') $this->getUserDetail('customerid')
]; ];
$customer_stdsubs = [
$this->getUserDetail('customerid') => $this->getUserDetail('standardsubdomain')
];
$select_fields = [ $select_fields = [
'`d`.`id`', '`d`.`id`',
@@ -949,7 +944,8 @@ class SubDomains extends ApiCommand implements ResourceEntity
'`d`.`parentdomainid`', '`d`.`parentdomainid`',
'`d`.`letsencrypt`', '`d`.`letsencrypt`',
'`d`.`registration_date`', '`d`.`registration_date`',
'`d`.`termination_date`' '`d`.`termination_date`',
'`d`.`deactivated`'
]; ];
} }
$query_fields = []; $query_fields = [];
@@ -963,7 +959,7 @@ class SubDomains extends ApiCommand implements ResourceEntity
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `pd` ON `pd`.`id`=`d`.`parentdomainid`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ") WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0' AND `d`.`email_only` = '0'
AND `d`.`id` NOT IN (" . implode(', ', $customer_stdsubs) . ")" . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit()); " . $this->getSearchWhere($query_fields, true) . " GROUP BY `d`.`id` ORDER BY `parentdomainname` ASC, `d`.`parentdomainid` ASC " . $this->getOrderBy(true) . $this->getLimit());
$result = []; $result = [];
Database::pexecute($domains_stmt, $query_fields, true, true); Database::pexecute($domains_stmt, $query_fields, true, true);

View File

@@ -92,7 +92,7 @@ class SysLog extends ApiCommand implements ResourceEntity
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list log-entries"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list log-entries");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -166,7 +166,7 @@ class Traffic extends ApiCommand implements ResourceEntity
$row['mail'] *= 1024; $row['mail'] *= 1024;
$result[] = $row; $result[] = $row;
} }
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] list traffic"); $this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_INFO, "[API] list traffic");
return $this->response([ return $this->response([
'count' => count($result), 'count' => count($result),
'list' => $result 'list' => $result

View File

@@ -112,11 +112,11 @@ class FroxlorRPC
* *
* @return bool * @return bool
*/ */
private static function validateAllowedFrom(array $allowed_from, string $remote_addr): bool public static function validateAllowedFrom(array $allowed_from, string $remote_addr): bool
{ {
// shorten IP for comparison // shorten IP for comparison
$remote_addr = inet_ntop(inet_pton($remote_addr)); $remote_addr = inet_ntop(inet_pton($remote_addr));
// check for diret matches // check for direct matches
if (in_array($remote_addr, $allowed_from)) { if (in_array($remote_addr, $allowed_from)) {
return true; return true;
} }

View File

@@ -166,6 +166,9 @@ final class MasterCron extends CliCommand
FroxlorLogger::getInstanceOf()->setCronLog(0); FroxlorLogger::getInstanceOf()->setCronLog(0);
} }
// clean up possible old login-links
Database::query("DELETE FROM `" . TABLE_PANEL_LOGINLINKS . "` WHERE `valid_until` < UNIX_TIMESTAMP()");
return $result; return $result;
} }

View File

@@ -62,8 +62,8 @@ class Bind extends DnsBase
$this->bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n"; $this->bindconf_file = '# ' . Settings::Get('system.bindconf_directory') . 'froxlor_bind.conf' . "\n" . '# Created ' . date('d.m.Y H:i') . "\n" . '# Do NOT manually edit this file, all changes will be deleted after the next domain change at the panel.' . "\n\n";
foreach ($domains as $domain) { foreach ($domains as $domain) {
if ($domain['ismainbutsubto'] > 0) { if ($domain['is_child']) {
// domains with ismainbutsubto>0 are handled by recursion within walkDomainList() // domains that are subdomains to other main domains are handled by recursion within walkDomainList()
continue; continue;
} }
$this->walkDomainList($domain, $domains); $this->walkDomainList($domain, $domains);
@@ -114,7 +114,7 @@ class Bind extends DnsBase
$isFroxlorHostname = true; $isFroxlorHostname = true;
} }
if ($domain['ismainbutsubto'] == 0) { if (!$domain['is_child']) {
$zoneContent = (string)Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); $zoneContent = (string)Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname);
$domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone'; $domain['zonefile'] = 'domains/' . $domain['domain'] . '.zone';
$zonefile_name = FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']); $zonefile_name = FileDir::makeCorrectFile(Settings::Get('system.bindconf_directory') . '/' . $domain['zonefile']);

View File

@@ -26,6 +26,7 @@
namespace Froxlor\Cron\Dns; namespace Froxlor\Cron\Dns;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Domain\Domain;
use Froxlor\FileDir; use Froxlor\FileDir;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
@@ -210,7 +211,6 @@ abstract class DnsBase
`d`.`dkim`, `d`.`dkim`,
`d`.`dkim_id`, `d`.`dkim_id`,
`d`.`dkim_pubkey`, `d`.`dkim_pubkey`,
`d`.`ismainbutsubto`,
`c`.`loginname`, `c`.`loginname`,
`c`.`guid` `c`.`guid`
FROM FROM
@@ -219,7 +219,7 @@ abstract class DnsBase
WHERE WHERE
`d`.`isbinddomain` = '1' `d`.`isbinddomain` = '1'
ORDER BY ORDER BY
`d`.`domain` ASC LENGTH(`d`.`domain`), `d`.`domain` ASC
"); ");
$domains = []; $domains = [];
@@ -241,7 +241,6 @@ abstract class DnsBase
'bindserial' => date('Ymd') . '00', 'bindserial' => date('Ymd') . '00',
'dkim' => '0', 'dkim' => '0',
'iswildcarddomain' => '1', 'iswildcarddomain' => '1',
'ismainbutsubto' => '0',
'zonefile' => '', 'zonefile' => '',
'froxlorhost' => '1' 'froxlorhost' => '1'
]; ];
@@ -257,18 +256,23 @@ abstract class DnsBase
if (!isset($domains[$key]['children'])) { if (!isset($domains[$key]['children'])) {
$domains[$key]['children'] = []; $domains[$key]['children'] = [];
} }
if ($domains[$key]['ismainbutsubto'] > 0) { if (!isset($domains[$key]['is_child'])) {
if (isset($domains[$domains[$key]['ismainbutsubto']])) { $domains[$key]['is_child'] = false;
$domains[$domains[$key]['ismainbutsubto']]['children'][] = $domains[$key]['id']; }
} else { $children = Domain::getMainSubdomainIds($key);
$domains[$key]['ismainbutsubto'] = 0; if (count($children) > 0) {
foreach ($children as $child) {
if (isset($domains[$child])) {
$domains[$key]['children'][] = $domains[$child]['id'];
$domains[$child]['is_child'] = true;
}
} }
} }
} }
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . 'ismainbutsubto ' . str_pad('parent domain', 40, ' ') . "list of child domain ids"); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, str_pad('domId', 9, ' ') . str_pad('domain', 40, ' ') . "list of child domain ids");
foreach ($domains as $domain) { foreach ($domains as $domain) {
$logLine = str_pad($domain['id'], 9, ' ') . str_pad($domain['domain'], 40, ' ') . str_pad($domain['ismainbutsubto'], 15, ' ') . str_pad(((isset($domains[$domain['ismainbutsubto']])) ? $domains[$domain['ismainbutsubto']]['domain'] : '-'), 40, ' ') . join(', ', $domain['children']); $logLine = str_pad($domain['id'], 9, ' ') . str_pad($domain['domain'], 40, ' ') . join(', ', $domain['children']);
$this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $logLine); $this->logger->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, $logLine);
} }

View File

@@ -50,8 +50,8 @@ class PowerDNS extends DnsBase
} }
foreach ($domains as $domain) { foreach ($domains as $domain) {
if ($domain['ismainbutsubto'] > 0) { if ($domain['is_child']) {
// domains with ismainbutsubto>0 are handled by recursion within walkDomainList() // domains that are subdomains to other main domains are handled by recursion within walkDomainList()
continue; continue;
} }
$this->walkDomainList($domain, $domains); $this->walkDomainList($domain, $domains);
@@ -108,7 +108,7 @@ class PowerDNS extends DnsBase
$isFroxlorHostname = true; $isFroxlorHostname = true;
} }
if ($domain['ismainbutsubto'] == 0) { if (!$domain['is_child']) {
$zoneContent = Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname); $zoneContent = Dns::createDomainZone(($domain['id'] == 'none') ? $domain : $domain['id'], $isFroxlorHostname);
if (count($subzones)) { if (count($subzones)) {
foreach ($subzones as $subzone) { foreach ($subzones as $subzone) {

View File

@@ -630,29 +630,6 @@ class Apache extends HttpConfigBase
} }
} }
/**
* Get the filename for the virtualhost
*/
protected function getVhostFilename($domain, $ssl_vhost = false)
{
if ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && ((int)$domain['ismainbutsubto'] == 0 || Domain::domainMainToSubExists($domain['ismainbutsubto']) == false)) {
$vhost_no = '35';
} elseif ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && (int)$domain['ismainbutsubto'] > 0) {
$vhost_no = '30';
} else {
// number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth
$vhost_no = (string)(30 - substr_count($domain['domain'], ".") + 1);
}
if ($ssl_vhost === true) {
$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf');
} else {
$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf');
}
return $vhost_filename;
}
/** /**
* We compose the virtualhost entry for one domain * We compose the virtualhost entry for one domain
*/ */

View File

@@ -28,6 +28,7 @@ namespace Froxlor\Cron\Http;
use Froxlor\Cron\Http\LetsEncrypt\AcmeSh; use Froxlor\Cron\Http\LetsEncrypt\AcmeSh;
use Froxlor\Cron\Http\Php\Fpm; use Froxlor\Cron\Http\Php\Fpm;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Domain\Domain;
use Froxlor\FileDir; use Froxlor\FileDir;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
@@ -187,4 +188,18 @@ class HttpConfigBase
} }
return false; return false;
} }
/**
* Get the filename for the virtualhost
*/
protected function getVhostFilename(array $domain, bool $ssl_vhost = false, bool $filename_only = false)
{
// number of dots in a domain specifies its position (and depth of subdomain) starting at 35 going downwards on higher depth
$vhost_no = (string)(35 - substr_count($domain['domain'], ".") + 1);
$filename = $vhost_no . '_froxlor_' . ($ssl_vhost ? 'ssl' : 'normal') . '_vhost_' . $domain['domain'] . '.conf';
if ($filename_only) {
return $filename;
}
return FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $filename);
}
} }

View File

@@ -336,24 +336,9 @@ class Lighttpd extends HttpConfigBase
$_pos = strrpos($_tmp_path, '/'); $_pos = strrpos($_tmp_path, '/');
$_inc_path = substr($_tmp_path, $_pos + 1); $_inc_path = substr($_tmp_path, $_pos + 1);
// maindomain $filename = self::getVhostFilename($domain, ($ssl == '1'), true);
if ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && ((int)$domain['ismainbutsubto'] == 0 || Domain::domainMainToSubExists($domain['ismainbutsubto']) == false)) { $vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/vhosts/' . $filename);
$vhost_no = '50'; $included_vhosts[] = $_inc_path . '/vhosts/' . $filename;
} elseif ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && (int)$domain['ismainbutsubto'] > 0) {
// sub-but-main-domain
$vhost_no = '51';
} else {
// subdomains
// number of dots in a domain specifies it's position (and depth of subdomain) starting at 89 going downwards on higher depth
$vhost_no = (string)(90 - substr_count($domain['domain'], ".") + 1);
}
if ($ssl == '1') {
$vhost_no = (int)$vhost_no += 10;
}
$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/vhosts/' . $vhost_no . '_' . $domain['domain'] . '.conf');
$included_vhosts[] = $_inc_path . '/vhosts/' . $vhost_no . '_' . $domain['domain'] . '.conf';
} }
if (!isset($this->lighttpd_data[$vhost_filename])) { if (!isset($this->lighttpd_data[$vhost_filename])) {

View File

@@ -225,7 +225,7 @@ class Nginx extends HttpConfigBase
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
// protect bin/ // protect bin/
$this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . 'location ~ ^' . rtrim($relpath, "/") . '/(bin|cache|logs|tests|vendor) {' . "\n";
$this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . ' deny all;' . "\n";
$this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n"; $this->nginx_data[$vhost_filename] .= "\t" . '}' . "\n";
} }
@@ -467,26 +467,6 @@ class Nginx extends HttpConfigBase
} }
} }
protected function getVhostFilename($domain, $ssl_vhost = false)
{
if ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && ((int)$domain['ismainbutsubto'] == 0 || Domain::domainMainToSubExists($domain['ismainbutsubto']) == false)) {
$vhost_no = '35';
} elseif ((int)$domain['parentdomainid'] == 0 && Domain::isCustomerStdSubdomain((int)$domain['id']) == false && (int)$domain['ismainbutsubto'] > 0) {
$vhost_no = '30';
} else {
// number of dots in a domain specifies it's position (and depth of subdomain) starting at 29 going downwards on higher depth
$vhost_no = (string)(30 - substr_count($domain['domain'], ".") + 1);
}
if ($ssl_vhost === true) {
$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_ssl_vhost_' . $domain['domain'] . '.conf');
} else {
$vhost_filename = FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $vhost_no . '_froxlor_normal_vhost_' . $domain['domain'] . '.conf');
}
return $vhost_filename;
}
protected function getVhostContent($domain, $ssl_vhost = false) protected function getVhostContent($domain, $ssl_vhost = false)
{ {
if ($ssl_vhost === true && $domain['ssl'] != '1' && $domain['ssl_redirect'] != '1') { if ($ssl_vhost === true && $domain['ssl'] != '1' && $domain['ssl_redirect'] != '1') {

View File

@@ -289,7 +289,7 @@ pm.max_children = 1
} }
} }
// now check if 'sendmail_path' has not beed set in the custom-php.ini // now check if 'sendmail_path' has not been set in the custom-php.ini
// if not we use our fallback-default as usual // if not we use our fallback-default as usual
if (strpos($fpm_config, 'php_admin_value[sendmail_path]') === false) { if (strpos($fpm_config, 'php_admin_value[sendmail_path]') === false) {
$fpm_config .= 'php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f ' . $this->domain['email'] . "\n"; $fpm_config .= 'php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f ' . $this->domain['email'] . "\n";

View File

@@ -25,10 +25,10 @@
namespace Froxlor; namespace Froxlor;
use Froxlor\Database\Database;
use Froxlor\UI\Collection;
use Froxlor\Api\Commands\Customers; use Froxlor\Api\Commands\Customers;
use Froxlor\Api\Commands\SubDomains; use Froxlor\Api\Commands\SubDomains;
use Froxlor\Database\Database;
use Froxlor\UI\Collection;
/** /**
* Class to manage the current user / session * Class to manage the current user / session
@@ -144,22 +144,29 @@ class CurrentUser
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT COUNT(`id`) as emaildomains SELECT COUNT(`id`) as emaildomains
FROM `" . TABLE_PANEL_DOMAINS . "` FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `customerid`= :cid AND `isemaildomain` = '1' WHERE `customerid`= :cid AND `isemaildomain` = '1' AND `deactivated` = '0'
"); ");
$result = Database::pexecute_first($result_stmt, [ $result = Database::pexecute_first($result_stmt, [
"cid" => $_SESSION['userinfo']['customerid'] "cid" => $_SESSION['userinfo']['customerid']
]); ]);
$addition = $result['emaildomains'] != 0; $addition = $result['emaildomains'] != 0;
} elseif ($resource == 'subdomains') { } elseif ($resource == 'subdomains') {
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'], $parentDomainCollection = (new Collection(
['sql_search' => ['d.parentdomainid' => 0]])); SubDomains::class,
$addition = $parentDomainCollection != 0; $_SESSION['userinfo'],
['sql_search' => [
'd.parentdomainid' => 0,
'd.deactivated' => 0,
'd.id' => ['op' => '<>', 'value' => $_SESSION['userinfo']['standardsubdomain']]
]
]
));
$addition = $parentDomainCollection->count() != 0;
} elseif ($resource == 'domains') { } elseif ($resource == 'domains') {
$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo'])); $customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));
$addition = $customerCollection != 0; $addition = $customerCollection->count() != 0;
} }
return ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition; return ($_SESSION['userinfo'][$resource . '_used'] < $_SESSION['userinfo'][$resource] || $_SESSION['userinfo'][$resource] == '-1') && $addition;
} }
} }

View File

@@ -235,51 +235,30 @@ class Domain
} }
/** /**
* check whether a domain has subdomains added as full-domains * get ids of domains that are main domains but a subdomain of another main domain (for DNS)
* #329
* *
* @param int $id domain-id * @param int $id main-domain to check
* *
* @return bool * @return array
* @throws \Exception * @throws \Exception
*/ */
public static function domainHasMainSubDomains(int $id): bool public static function getMainSubdomainIds(int $id): array
{ {
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT COUNT(`id`) as `mainsubs` FROM `" . TABLE_PANEL_DOMAINS . "` SELECT id
WHERE `ismainbutsubto` = :id"); FROM `" . TABLE_PANEL_DOMAINS . "`
$result = Database::pexecute_first($result_stmt, [ WHERE
'id' => $id isbinddomain = 1 AND
]); domain LIKE CONCAT('%.', ( SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` AS d WHERE d.id = :id ))
");
if ($result && isset($result['mainsubs'])) {
return $result['mainsubs'] > 0;
}
return false;
}
/**
* check whether a subof-domain exists
* #329
*
* @param int $id subof-domain-id
*
* @return bool
* @throws \Exception
*/
public static function domainMainToSubExists(int $id): bool
{
$result_stmt = Database::prepare("
SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `id` = :id");
Database::pexecute($result_stmt, [ Database::pexecute($result_stmt, [
'id' => $id 'id' => $id
]); ]);
$result = $result_stmt->fetch(PDO::FETCH_ASSOC); $result = [];
while ($entry = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
if ($result && isset($result['id'])) { $result = $entry['id'];
return $result['id'] > 0;
} }
return false; return $result;
} }
/** /**

View File

@@ -31,10 +31,10 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '2.0.19'; const VERSION = '2.1.0-dev1';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202304260'; const DBVERSION = '202305240';
// Distribution branding-tag (used for Debian etc.) // Distribution branding-tag (used for Debian etc.)
const BRANDING = ''; const BRANDING = '';

View File

@@ -421,6 +421,7 @@ class Core
$this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed'); $this->updateSetting($upd_stmt, $this->validatedData['activate_newsfeed'], 'admin', 'show_news_feed');
$this->updateSetting($upd_stmt, dirname(__FILE__, 5), 'system', 'letsencryptchallengepath'); $this->updateSetting($upd_stmt, dirname(__FILE__, 5), 'system', 'letsencryptchallengepath');
$this->updateSetting($upd_stmt, dirname(__FILE__, 5) . '/templates/misc/deactivated/', 'system', 'deactivateddocroot');
// insert the lastcronrun to be the installation date // insert the lastcronrun to be the installation date
$this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun'); $this->updateSetting($upd_stmt, time(), 'system', 'lastcronrun');

View File

@@ -51,6 +51,9 @@ class Domain
public static function domainTarget(array $attributes) public static function domainTarget(array $attributes)
{ {
if (empty($attributes['fields']['aliasdomain'])) { if (empty($attributes['fields']['aliasdomain'])) {
if ($attributes['fields']['deactivated']) {
return lng('admin.deactivated');
}
// path or redirect // path or redirect
if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) { if (preg_match('/^https?\:\/\//', $attributes['fields']['documentroot'])) {
return [ return [
@@ -80,7 +83,7 @@ class Domain
} }
$result .= '<a href="http://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>'; $result .= '<a href="http://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
// check for statistics if parentdomainid==0 to show stats-link for customers // check for statistics if parentdomainid==0 to show stats-link for customers
if ((int)UI::getCurrentUser()['adminsession'] == 0 && $attributes['fields']['parentdomainid'] == 0) { if ((int)UI::getCurrentUser()['adminsession'] == 0 && $attributes['fields']['parentdomainid'] == 0 && $attributes['fields']['deactivated'] == 0) {
$statsapp = Settings::Get('system.traffictool'); $statsapp = Settings::Get('system.traffictool');
$result .= ' <a href="http://' . $attributes['data'] . '/' . $statsapp . '" rel="external" target="_blank" title="' . lng('domains.statstics') . '"><i class="fa-solid fa-chart-line text-secondary"></i></a>'; $result .= ' <a href="http://' . $attributes['data'] . '/' . $statsapp . '" rel="external" target="_blank" title="' . lng('domains.statstics') . '"><i class="fa-solid fa-chart-line text-secondary"></i></a>';
} }
@@ -95,12 +98,12 @@ class Domain
public static function canEdit(array $attributes): bool public static function canEdit(array $attributes): bool
{ {
return (bool)$attributes['fields']['caneditdomain']; return (bool)($attributes['fields']['caneditdomain'] && !$attributes['fields']['deactivated']);
} }
public static function canViewLogs(array $attributes): bool public static function canViewLogs(array $attributes): bool
{ {
if ((int)$attributes['fields']['email_only'] == 0) { if ((int)$attributes['fields']['email_only'] == 0 && !$attributes['fields']['deactivated']) {
if ((int)UI::getCurrentUser()['adminsession'] == 0 && (bool)UI::getCurrentUser()['logviewenabled']) { if ((int)UI::getCurrentUser()['adminsession'] == 0 && (bool)UI::getCurrentUser()['logviewenabled']) {
return true; return true;
} elseif ((int)UI::getCurrentUser()['adminsession'] == 1) { } elseif ((int)UI::getCurrentUser()['adminsession'] == 1) {
@@ -129,7 +132,8 @@ class Domain
&& UI::getCurrentUser()['dnsenabled'] == '1' && UI::getCurrentUser()['dnsenabled'] == '1'
&& $attributes['fields']['caneditdomain'] == '1' && $attributes['fields']['caneditdomain'] == '1'
&& Settings::Get('system.bind_enable') == '1' && Settings::Get('system.bind_enable') == '1'
&& Settings::Get('system.dnsenabled') == '1'; && Settings::Get('system.dnsenabled') == '1'
&& !$attributes['fields']['deactivated'];
} }
public static function adminCanEditDNS(array $attributes): bool public static function adminCanEditDNS(array $attributes): bool
@@ -152,6 +156,7 @@ class Domain
&& (int)$attributes['fields']['caneditdomain'] == 1 && (int)$attributes['fields']['caneditdomain'] == 1
&& (int)$attributes['fields']['letsencrypt'] == 0 && (int)$attributes['fields']['letsencrypt'] == 0
&& (int)$attributes['fields']['email_only'] == 0 && (int)$attributes['fields']['email_only'] == 0
&& !$attributes['fields']['deactivated']
) { ) {
return true; return true;
} }

View File

@@ -60,17 +60,18 @@ class Style
$today = time(); $today = time();
$termination_css = 'bg-warning'; $termination_css = 'bg-warning';
if ($cdate < $today) { if ($cdate < $today) {
$termination_css = 'bg-danger'; $termination_css = 'bg-danger text-light';
} }
} }
return $attributes['fields']['deactivated'] ? 'bg-info' : $termination_css; $deactivated = $attributes['fields']['deactivated'] || $attributes['fields']['customer_deactivated'];
return $deactivated ? 'bg-info text-light' : $termination_css;
} }
public static function resultCustomerLockedOrDeactivated(array $attributes): string public static function resultCustomerLockedOrDeactivated(array $attributes): string
{ {
$row_css = ''; $row_css = '';
if ((int)$attributes['fields']['deactivated'] == 1) { if ((int)$attributes['fields']['deactivated'] == 1) {
$row_css = 'bg-info'; $row_css = 'bg-info text-light';
} elseif ( } elseif (
$attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts') $attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')
&& $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime')) && $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))

View File

@@ -25,10 +25,13 @@
namespace Froxlor\UI\Callbacks; namespace Froxlor\UI\Callbacks;
use Froxlor\CurrentUser;
use Froxlor\Database\Database;
use Froxlor\Froxlor; use Froxlor\Froxlor;
use Froxlor\PhpHelper; use Froxlor\PhpHelper;
use Froxlor\UI\Panel\UI; use Froxlor\UI\Panel\UI;
use Froxlor\User; use Froxlor\User;
use PDO;
class Text class Text
{ {
@@ -105,4 +108,44 @@ class Text
'body' => $body 'body' => $body
]; ];
} }
public static function domainDuplicateModal(array $attributes): array
{
$linker = UI::getLinker();
$result = $attributes['fields'];
$customers = [
0 => lng('panel.please_choose')
];
$result_customers_stmt = Database::prepare("
SELECT `customerid`, `loginname`, `name`, `firstname`, `company`
FROM `" . TABLE_PANEL_CUSTOMERS . "` " . (CurrentUser::getField('customers_see_all') ? '' : " WHERE `adminid` = :adminid ") . "
ORDER BY COALESCE(NULLIF(`name`,''), `company`) ASC
");
$params = [];
if (CurrentUser::getField('customers_see_all') == '0') {
$params['adminid'] = CurrentUser::getField('adminid');
}
Database::pexecute($result_customers_stmt, $params);
while ($row_customer = $result_customers_stmt->fetch(PDO::FETCH_ASSOC)) {
$customers[$row_customer['customerid']] = User::getCorrectFullUserDetails($row_customer) . ' (' . $row_customer['loginname'] . ')';
}
$domdup_data = include Froxlor::getInstallDir() . '/lib/formfields/admin/domains/formfield.domains_duplicate.php';
$body = UI::twig()->render(UI::validateThemeTemplate('/user/inline-form.html.twig'), [
'formaction' => $linker->getLink(['section' => 'domains', 'page' => 'domains', 'action' => 'duplicate']),
'formdata' => $domdup_data['domain_duplicate'],
'editid' => $attributes['fields']['id'],
'nosubmit' => 0
]);
return [
'entry' => $attributes['fields']['id'],
'id' => 'ddModal' . $attributes['fields']['id'],
'title' => lng('admin.domain_duplicate_named', [$attributes['fields']['domain']]),
'action' => 'duplicate',
'body' => $body
];
}
} }

View File

@@ -230,6 +230,7 @@ class Listing
'label' => $coldata['label'], 'label' => $coldata['label'],
'checked' => in_array($column, $tabellisting['visible_columns']), 'checked' => in_array($column, $tabellisting['visible_columns']),
'searchable' => $coldata['searchable'] ?? true, 'searchable' => $coldata['searchable'] ?? true,
'isdefaultsearchfield' => $coldata['isdefaultsearchfield'] ?? false,
]; ];
} }
} }

View File

@@ -52,16 +52,16 @@ class Check
]; ];
$check_array = [ $check_array = [
'system_mod_fcgid_enabled' => [ 'system_mod_fcgid' => [
'other_post_field' => 'system_phpfpm_enabled', 'other_post_field' => 'phpfpm_enabled',
'other_enabled' => 'phpfpm.enabled', 'other_enabled' => 'phpfpm.enabled',
'other_enabled_lng' => 'phpfpmstillenabled', 'other_enabled_lng' => 'phpfpmstillenabled',
'deactivate' => [ 'deactivate' => [
'phpfpm.enabled_ownvhost' => 0 'phpfpm.enabled_ownvhost' => 0
] ]
], ],
'system_phpfpm_enabled' => [ 'phpfpm_enabled' => [
'other_post_field' => 'system_mod_fcgid_enabled', 'other_post_field' => 'system_mod_fcgid',
'other_enabled' => 'system.mod_fcgid', 'other_enabled' => 'system.mod_fcgid',
'other_enabled_lng' => 'fcgidstillenabled', 'other_enabled_lng' => 'fcgidstillenabled',
'deactivate' => [ 'deactivate' => [

View File

@@ -10,11 +10,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -10,11 +10,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -10,11 +10,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -10,11 +10,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -9,12 +9,14 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_diroptions" value="/etc/lighttpd/froxlor-diroptions/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_diroptions" value="/etc/lighttpd/froxlor-diroptions/"></default>
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>+
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -17,11 +17,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_diroptions" value="/etc/lighttpd/diropts.d/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_diroptions" value="/etc/lighttpd/diropts.d/"></default>
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="/etc/init.d/lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="/etc/init.d/lighttpd reload"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="/etc/init.d/nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="/etc/init.d/nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
<default settinggroup="system" varname="bindreload_command" value="/etc/init.d/named restart"></default> <default settinggroup="system" varname="bindreload_command" value="/etc/init.d/named restart"></default>
<default settinggroup="system" varname="crondreload" value="/etc/init.d/cronie restart"></default> <default settinggroup="system" varname="crondreload" value="/etc/init.d/cronie restart"></default>
</defaults> </defaults>

View File

@@ -10,11 +10,13 @@
<default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default> <default for="lighttpd" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/lighttpd/froxlor-htpasswd/"></default>
<default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default> <default for="lighttpd" settinggroup="system" varname="apachereload_command" value="service lighttpd reload"></default>
<default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default> <default for="lighttpd" settinggroup="system" varname="letsencryptacmeconf" value="/etc/lighttpd/acme.conf"></default>
<default for="lighttpd" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/lighttpd/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_vhost" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_diroptions" value="/etc/nginx/sites-enabled/"></default>
<default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default> <default for="nginx" settinggroup="system" varname="apacheconf_htpasswddir" value="/etc/nginx/froxlor-htpasswd/"></default>
<default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default> <default for="nginx" settinggroup="system" varname="apachereload_command" value="service nginx reload"></default>
<default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default> <default for="nginx" settinggroup="system" varname="letsencryptacmeconf" value="/etc/nginx/acme.conf"></default>
<default for="nginx" settinggroup="phpfpm" varname="fastcgi_ipcdir" value="/var/run/nginx/"></default>
</defaults> </defaults>
<services> <services>
<!-- HTTP --> <!-- HTTP -->

View File

@@ -133,7 +133,7 @@ return [
'customers' => [ 'customers' => [
'label' => lng('admin.customers'), 'label' => lng('admin.customers'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['customers'], 'value' => empty($result['customers']) ? '0' : $result['customers'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -146,7 +146,7 @@ return [
'domains' => [ 'domains' => [
'label' => lng('admin.domains'), 'label' => lng('admin.domains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['domains'], 'value' => empty($result['domains']) ? '0' : $result['domains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
@@ -159,49 +159,49 @@ return [
'diskspace' => [ 'diskspace' => [
'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.diskspace') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['diskspace'], 'value' => empty($result['diskspace']) ? '0' : $result['diskspace'],
'maxlength' => 6, 'maxlength' => 6,
'mandatory' => true 'mandatory' => true
], ],
'traffic' => [ 'traffic' => [
'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')', 'label' => lng('customer.traffic') . ' (' . lng('customer.gib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['traffic'], 'value' => empty($result['traffic']) ? '0' : $result['traffic'],
'maxlength' => 4, 'maxlength' => 4,
'mandatory' => true 'mandatory' => true
], ],
'subdomains' => [ 'subdomains' => [
'label' => lng('customer.subdomains'), 'label' => lng('customer.subdomains'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['subdomains'], 'value' => empty($result['subdomains']) ? '0' : $result['subdomains'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'emails' => [ 'emails' => [
'label' => lng('customer.emails'), 'label' => lng('customer.emails'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['emails'], 'value' => empty($result['emails']) ? '0' : $result['emails'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_accounts' => [ 'email_accounts' => [
'label' => lng('customer.accounts'), 'label' => lng('customer.accounts'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_accounts'], 'value' => empty($result['email_accounts']) ? '0' : $result['email_accounts'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_forwarders' => [ 'email_forwarders' => [
'label' => lng('customer.forwarders'), 'label' => lng('customer.forwarders'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_forwarders'], 'value' => empty($result['email_forwarders']) ? '0' : $result['email_forwarders'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
], ],
'email_quota' => [ 'email_quota' => [
'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')', 'label' => lng('customer.email_quota') . ' (' . lng('customer.mib') . ')',
'type' => 'textul', 'type' => 'textul',
'value' => $result['email_quota'], 'value' => empty($result['email_quota']) ? '0' : $result['email_quota'],
'maxlength' => 9, 'maxlength' => 9,
'visible' => Settings::Get('system.mail_quota_enabled') == '1', 'visible' => Settings::Get('system.mail_quota_enabled') == '1',
'mandatory' => true 'mandatory' => true
@@ -209,13 +209,13 @@ return [
'ftps' => [ 'ftps' => [
'label' => lng('customer.ftps'), 'label' => lng('customer.ftps'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['ftps'], 'value' => empty($result['ftps']) ? '0' : $result['ftps'],
'maxlength' => 9 'maxlength' => 9
], ],
'mysqls' => [ 'mysqls' => [
'label' => lng('customer.mysqls'), 'label' => lng('customer.mysqls'),
'type' => 'textul', 'type' => 'textul',
'value' => $result['mysqls'], 'value' => empty($result['mysqls']) ? '0' : $result['mysqls'],
'maxlength' => 9, 'maxlength' => 9,
'mandatory' => true 'mandatory' => true
] ]

View File

@@ -60,12 +60,6 @@ return [
'type' => 'select', 'type' => 'select',
'select_var' => $domains 'select_var' => $domains
], ],
'issubof' => [
'label' => lng('domains.issubof'),
'desc' => lng('domains.issubofinfo'),
'type' => 'select',
'select_var' => $subtodomains
],
'caneditdomain' => [ 'caneditdomain' => [
'label' => lng('admin.domain_editable.title'), 'label' => lng('admin.domain_editable.title'),
'desc' => lng('admin.domain_editable.desc'), 'desc' => lng('admin.domain_editable.desc'),

View File

@@ -65,13 +65,6 @@ return [
'select_var' => $domains, 'select_var' => $domains,
'selected' => $result['aliasdomain'] 'selected' => $result['aliasdomain']
], ],
'issubof' => [
'label' => lng('domains.issubof'),
'desc' => lng('domains.issubofinfo'),
'type' => 'select',
'select_var' => $subtodomains,
'selected' => $result['ismainbutsubto']
],
'associated_info' => [ 'associated_info' => [
'label' => lng('domains.associated_with_domain'), 'label' => lng('domains.associated_with_domain'),
'type' => 'label', 'type' => 'label',
@@ -104,7 +97,13 @@ return [
'type' => 'date', 'type' => 'date',
'value' => $result['termination_date'], 'value' => $result['termination_date'],
'size' => 10 'size' => 10
] ],
'deactivated' => [
'label' => lng('admin.deactivated'),
'type' => 'checkbox',
'value' => '1',
'checked' => $result['deactivated']
],
] ]
], ],
'section_e' => [ 'section_e' => [

View File

@@ -43,13 +43,16 @@ return [
'email_password' => [ 'email_password' => [
'label' => lng('login.password'), 'label' => lng('login.password'),
'type' => 'password', 'type' => 'password',
'autocomplete' => 'off' 'autocomplete' => 'off',
], 'next_to' => [
'email_password_suggestion' => [ 'email_password_suggestion' => [
'label' => lng('customer.generated_pwd'), 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),
'value' => Crypt::generatePassword() 'value' => Crypt::generatePassword(),
'readonly' => true
]
]
] ]
] ]
] ]

View File

@@ -46,7 +46,7 @@ return [
'autocomplete' => 'off', 'autocomplete' => 'off',
'mandatory' => true, 'mandatory' => true,
'next_to' => [ 'next_to' => [
'admin_password_suggestion' => [ 'email_password_suggestion' => [
'next_to_prefix' => lng('customer.generated_pwd') . ':', 'next_to_prefix' => lng('customer.generated_pwd') . ':',
'type' => 'text', 'type' => 'text',
'visible' => (Settings::Get('panel.password_regex') == ''), 'visible' => (Settings::Get('panel.password_regex') == ''),

View File

@@ -186,7 +186,7 @@ return [
'url' => 'admin_customers.php?page=customers', 'url' => 'admin_customers.php?page=customers',
'label' => lng('admin.customers'), 'label' => lng('admin.customers'),
'required_resources' => 'customers', 'required_resources' => 'customers',
'add_shortlink' => CurrentUser::canAddResource('customers') ? 'admin_customers.php?page=customers&action=add' : null, 'add_shortlink' => CurrentUser::isAdmin() && CurrentUser::canAddResource('customers') ? 'admin_customers.php?page=customers&action=add' : null,
], ],
[ [
'url' => 'admin_admins.php?page=admins', 'url' => 'admin_admins.php?page=admins',
@@ -198,7 +198,7 @@ return [
'url' => 'admin_domains.php?page=domains', 'url' => 'admin_domains.php?page=domains',
'label' => lng('admin.domains'), 'label' => lng('admin.domains'),
'required_resources' => 'domains', 'required_resources' => 'domains',
'add_shortlink' => CurrentUser::canAddResource('domains') ? 'admin_domains.php?page=domains&action=add' : null, 'add_shortlink' => CurrentUser::isAdmin() && CurrentUser::canAddResource('domains') ? 'admin_domains.php?page=domains&action=add' : null,
], ],
[ [
'url' => 'admin_domains.php?page=sslcertificates', 'url' => 'admin_domains.php?page=sslcertificates',

View File

@@ -48,6 +48,7 @@ return [
'field' => 'loginname', 'field' => 'loginname',
'callback' => [Impersonate::class, 'admin'], 'callback' => [Impersonate::class, 'admin'],
'sortable' => true, 'sortable' => true,
'isdefaultsearchfield' => true,
], ],
'name' => [ 'name' => [
'label' => lng('customer.name'), 'label' => lng('customer.name'),

View File

@@ -48,6 +48,7 @@ return [
'd.domain_ace' => [ 'd.domain_ace' => [
'label' => lng('domains.domainname'), 'label' => lng('domains.domainname'),
'field' => 'domain_ace', 'field' => 'domain_ace',
'isdefaultsearchfield' => true,
], ],
'ipsandports' => [ 'ipsandports' => [
'label' => lng('admin.ipsandports.ipsandports'), 'label' => lng('admin.ipsandports.ipsandports'),
@@ -161,6 +162,11 @@ return [
'id' => ':id' 'id' => ':id'
], ],
], ],
'duplicate' => [
'icon' => 'fa-solid fa-clone',
'title' => lng('admin.domain_duplicate'),
'modal' => [Text::class, 'domainDuplicateModal'],
],
'logfiles' => [ 'logfiles' => [
'icon' => 'fa-solid fa-file', 'icon' => 'fa-solid fa-file',
'title' => lng('panel.viewlogs'), 'title' => lng('panel.viewlogs'),

View File

@@ -41,6 +41,7 @@ return [
'description' => [ 'description' => [
'label' => lng('admin.phpsettings.description'), 'label' => lng('admin.phpsettings.description'),
'field' => 'description', 'field' => 'description',
'isdefaultsearchfield' => true,
], ],
'configs' => [ 'configs' => [
'label' => lng('admin.phpsettings.activephpconfigs'), 'label' => lng('admin.phpsettings.activephpconfigs'),

View File

@@ -37,6 +37,7 @@ return [
'ip' => [ 'ip' => [
'label' => lng('admin.ipsandports.ip'), 'label' => lng('admin.ipsandports.ip'),
'field' => 'ip', 'field' => 'ip',
'isdefaultsearchfield' => true,
], ],
'port' => [ 'port' => [
'label' => lng('admin.ipsandports.port'), 'label' => lng('admin.ipsandports.port'),

View File

@@ -40,6 +40,7 @@ return [
'caption' => [ 'caption' => [
'label' => lng('admin.mysqlserver.caption'), 'label' => lng('admin.mysqlserver.caption'),
'field' => 'caption', 'field' => 'caption',
'isdefaultsearchfield' => true,
], ],
'host' => [ 'host' => [
'label' => lng('admin.mysqlserver.host'), 'label' => lng('admin.mysqlserver.host'),

View File

@@ -42,6 +42,7 @@ return [
'c.description' => [ 'c.description' => [
'label' => lng('admin.phpsettings.description'), 'label' => lng('admin.phpsettings.description'),
'field' => 'description', 'field' => 'description',
'isdefaultsearchfield' => true,
], ],
'domains' => [ 'domains' => [
'label' => lng('admin.phpsettings.activedomains'), 'label' => lng('admin.phpsettings.activedomains'),

View File

@@ -40,6 +40,7 @@ return [
'p.name' => [ 'p.name' => [
'label' => lng('admin.plans.name'), 'label' => lng('admin.plans.name'),
'field' => 'name', 'field' => 'name',
'isdefaultsearchfield' => true,
], ],
'p.description' => [ 'p.description' => [
'label' => lng('admin.plans.description'), 'label' => lng('admin.plans.description'),

View File

@@ -57,3 +57,4 @@ const TABLE_PANEL_FPMDAEMONS = 'panel_fpmdaemons';
const TABLE_PANEL_PLANS = 'panel_plans'; const TABLE_PANEL_PLANS = 'panel_plans';
const TABLE_API_KEYS = 'api_keys'; const TABLE_API_KEYS = 'api_keys';
const TABLE_PANEL_USERCOLUMNS = 'panel_usercolumns'; const TABLE_PANEL_USERCOLUMNS = 'panel_usercolumns';
const TABLE_PANEL_LOGINLINKS = 'panel_loginlinks';

View File

@@ -489,6 +489,8 @@ return [
'adminguide' => 'Admin Guide', 'adminguide' => 'Admin Guide',
'userguide' => 'User Guide', 'userguide' => 'User Guide',
'apiguide' => 'API Guide', 'apiguide' => 'API Guide',
'domain_duplicate' => 'Domain duplizieren',
'domain_duplicate_named' => '%s duplizieren',
], ],
'apikeys' => [ 'apikeys' => [
'no_api_keys' => 'Keine API Keys gefunden', 'no_api_keys' => 'Keine API Keys gefunden',
@@ -665,9 +667,6 @@ return [
'aliasdomains' => 'Aliasdomains', 'aliasdomains' => 'Aliasdomains',
'redirectifpathisurl' => 'Redirect-Code (Standard: leer)', 'redirectifpathisurl' => 'Redirect-Code (Standard: leer)',
'redirectifpathisurlinfo' => 'Der Redirect-Code kann gewählt werden, wenn der eingegebene Pfad eine URL ist.<br/><strong class="text-danger">HINWEIS:</strong> Änderungen werden nur wirksam wenn der Pfad eine URL ist.', 'redirectifpathisurlinfo' => 'Der Redirect-Code kann gewählt werden, wenn der eingegebene Pfad eine URL ist.<br/><strong class="text-danger">HINWEIS:</strong> Änderungen werden nur wirksam wenn der Pfad eine URL ist.',
'issubof' => 'Diese Domain ist eine Subdomain von der Domain',
'issubofinfo' => 'Diese Einstellung muss gesetzt werden, wenn Sie eine Subdomain einer Hauptdomain als Hauptdomain anlegen (z. B. soll "www.domain.tld" hinzugefügt werden, somit muss hier "domain.tld" ausgewählt werden).',
'nosubtomaindomain' => 'Keine Subdomain einer Hauptdomain',
'ipandport_multi' => [ 'ipandport_multi' => [
'title' => 'IP-Adresse(n)', 'title' => 'IP-Adresse(n)',
'description' => 'Definieren Sie eine oder mehrere IP-Adresse(n) für diese Domain.<br /><br /><div class="text-danger">Hinweis: Die IP-Adressen können nicht geändert werden, sollte die Domain als <strong>Alias-Domain</strong> für eine andere Domain konfiguriert worden sein.</div>', 'description' => 'Definieren Sie eine oder mehrere IP-Adresse(n) für diese Domain.<br /><br /><div class="text-danger">Hinweis: Die IP-Adressen können nicht geändert werden, sollte die Domain als <strong>Alias-Domain</strong> für eine andere Domain konfiguriert worden sein.</div>',
@@ -700,6 +699,7 @@ return [
'openbasedirenabled' => 'Openbasedir Einschränkung', 'openbasedirenabled' => 'Openbasedir Einschränkung',
'hsts' => 'HSTS aktiviert', 'hsts' => 'HSTS aktiviert',
'aliasdomainid' => 'ID der Alias-Domain', 'aliasdomainid' => 'ID der Alias-Domain',
'nodomainsassignedbyadmin' => 'Diesem Account wurde noch keine (aktive) Domain zugewiesen. Bitte kontaktiere deinen Administrator, wenn du der Meinung bist, das ist nicht korrekt.',
], ],
'emails' => [ 'emails' => [
'description' => 'Hier können Sie Ihre E-Mail-Adressen einrichten.<br />Ein Konto ist wie Ihr Briefkasten vor der Haustür. Wenn jemand eine E-Mail an Sie schreibt, wird diese in dieses Konto gelegt.<br /><br />Die Zugangsdaten lauten wie folgt: (Die Angaben in <i>kursiver</i> Schrift sind durch die jeweiligen Einträge zu ersetzen)<br /><br />Hostname: <b><i>Domainname</i></b><br />Benutzername: <b><i>Kontoname / E-Mail-Adresse</i></b><br />Passwort: <b><i>das gewählte Passwort</i></b>', 'description' => 'Hier können Sie Ihre E-Mail-Adressen einrichten.<br />Ein Konto ist wie Ihr Briefkasten vor der Haustür. Wenn jemand eine E-Mail an Sie schreibt, wird diese in dieses Konto gelegt.<br /><br />Die Zugangsdaten lauten wie folgt: (Die Angaben in <i>kursiver</i> Schrift sind durch die jeweiligen Einträge zu ersetzen)<br /><br />Hostname: <b><i>Domainname</i></b><br />Benutzername: <b><i>Kontoname / E-Mail-Adresse</i></b><br />Passwort: <b><i>das gewählte Passwort</i></b>',
@@ -770,6 +770,7 @@ return [
'domainisaliasorothercustomer' => 'Die ausgewählte Aliasdomain ist entweder selbst eine Aliasdomain, hat nicht die gleiche IP/Port-Kombination oder gehört einem anderen Kunden.', 'domainisaliasorothercustomer' => 'Die ausgewählte Aliasdomain ist entweder selbst eine Aliasdomain, hat nicht die gleiche IP/Port-Kombination oder gehört einem anderen Kunden.',
'emailexistalready' => 'Die E-Mail-Adresse "%s" existiert bereits.', 'emailexistalready' => 'Die E-Mail-Adresse "%s" existiert bereits.',
'maindomainnonexist' => 'Die Hauptdomain "%s" existiert nicht.', 'maindomainnonexist' => 'Die Hauptdomain "%s" existiert nicht.',
'maindomaindeactivated' => 'Die Hauptdomain "%s" ist deaktiviert.',
'destinationnonexist' => 'Bitte geben Sie Ihre Weiterleitungsadresse im Feld \'Nach\' ein.', 'destinationnonexist' => 'Bitte geben Sie Ihre Weiterleitungsadresse im Feld \'Nach\' ein.',
'destinationalreadyexistasmail' => 'Die Weiterleitung zu "%s" existiert bereits als aktive E-Mail-Adresse.', 'destinationalreadyexistasmail' => 'Die Weiterleitung zu "%s" existiert bereits als aktive E-Mail-Adresse.',
'destinationalreadyexist' => 'Es existiert bereits eine Weiterleitung nach "%s".', 'destinationalreadyexist' => 'Es existiert bereits eine Weiterleitung nach "%s".',
@@ -929,6 +930,7 @@ return [
'2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt', '2fa_wrongcode' => 'Der angegebene Code ist nicht korrekt',
'gnupgextensionnotavailable' => 'Die PHP GnuPG Extension ist nicht verfügbar. PGP Schlüssel können nicht validiert werden.', 'gnupgextensionnotavailable' => 'Die PHP GnuPG Extension ist nicht verfügbar. PGP Schlüssel können nicht validiert werden.',
'invalidpgppublickey' => 'Der angegebene PGP Public Key ist ungültig', 'invalidpgppublickey' => 'Der angegebene PGP Public Key ist ungültig',
'invalid_validtime' => 'Wert der valid_time in Sekunden muss zwischen 10 und 120 liegen.',
], ],
'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.',
@@ -1287,7 +1289,6 @@ Vielen Dank, Ihr Administrator',
'admin_quotas_reallyenforce' => 'Sind Sie sicher, dass Sie allen Benutzern das Default-Quota zuweisen wollen? Dies kann nicht rückgängig gemacht werden!', 'admin_quotas_reallyenforce' => 'Sind Sie sicher, dass Sie allen Benutzern das Default-Quota zuweisen wollen? Dies kann nicht rückgängig gemacht werden!',
'phpsetting_reallydelete' => 'Wollen Sie diese PHP-Einstellungen wirklich löschen? Alle Domains die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.', 'phpsetting_reallydelete' => 'Wollen Sie diese PHP-Einstellungen wirklich löschen? Alle Domains die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.',
'fpmsetting_reallydelete' => 'Wollen Sie diese PHP-FPM Einstellungen wirklich löschen? Alle PHP Konfigurationen die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.', 'fpmsetting_reallydelete' => 'Wollen Sie diese PHP-FPM Einstellungen wirklich löschen? Alle PHP Konfigurationen die diese Einstellungen bis jetzt verwendet haben, werden dann auf die Standardeinstellungen umgestellt.',
'remove_subbutmain_domains' => 'Auch Domains entfernen, welche als volle Domains hinzugefügt wurden, aber Subdomains von dieser Domain sind?',
'customer_reallyunlock' => 'Wollen Sie den Kunden "%s" wirklich entsperren?', 'customer_reallyunlock' => 'Wollen Sie den Kunden "%s" wirklich entsperren?',
'admin_integritycheck_reallyfix' => 'M&ouml;chten Sie wirklich versuchen s&auml;mtliche Datenbank-Integrit&auml;tsprobleme automatisch zu beheben?', 'admin_integritycheck_reallyfix' => 'M&ouml;chten Sie wirklich versuchen s&auml;mtliche Datenbank-Integrit&auml;tsprobleme automatisch zu beheben?',
'plan_reallydelete' => 'Wollen Sie den Hostingplan %s wirklich löschen?', 'plan_reallydelete' => 'Wollen Sie den Hostingplan %s wirklich löschen?',
@@ -2018,12 +2019,8 @@ Vielen Dank, Ihr Administrator',
'description' => 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/><br /><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!', 'description' => 'Der Inhalt dieses Feldes wird direkt in den IP/Port-vHost-Container übernommen. Die folgenden Variablen können verwendet werden:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (wenn zutreffend)<br/><br /><strong>ACHTUNG:</strong> Der Code wird nicht auf Fehler geprüft. Etwaige Fehler werden also auch übernommen. Der Webserver könnte nicht mehr starten!',
], ],
'includedefault_sslvhostconf' => 'Nicht-SSL vHost-Einstellungen in SSL-vHost inkludieren', 'includedefault_sslvhostconf' => 'Nicht-SSL vHost-Einstellungen in SSL-vHost inkludieren',
'apply_specialsettings_default' => [ 'apply_specialsettings_default' => 'Standardwert für "Übernehme Einstellungen für alle Subdomains (*.beispiel.de)" Einstellung beim Bearbeiten einer Domain',
'title' => 'Standardwert für "Übernehme Einstellungen für alle Subdomains (*.beispiel.de)\' Einstellung beim Bearbeiten einer Domain', 'apply_phpconfigs_default' => 'Standardwert für "PHP-Config für alle Subdomains übernehmen:" Einstellung beim Bearbeiten einer Domain',
],
'apply_phpconfigs_default' => [
'title' => 'Standardwert für "PHP-Config für alle Subdomains übernehmen:\' Einstellung beim Bearbeiten einer Domain',
],
'awstats' => [ 'awstats' => [
'logformat' => [ 'logformat' => [
'title' => 'LogFormat Einstellung', 'title' => 'LogFormat Einstellung',

View File

@@ -500,6 +500,8 @@ return [
'adminguide' => 'Admin guide', 'adminguide' => 'Admin guide',
'userguide' => 'User guide', 'userguide' => 'User guide',
'apiguide' => 'API guide', 'apiguide' => 'API guide',
'domain_duplicate' => 'Duplicate domain',
'domain_duplicate_named' => 'Duplicate %s',
'backups' => [ 'backups' => [
'backups' => 'Backups', 'backups' => 'Backups',
], ],
@@ -734,9 +736,6 @@ return [
'aliasdomains' => 'Alias domains', 'aliasdomains' => 'Alias domains',
'redirectifpathisurl' => 'Redirect code (default: empty)', 'redirectifpathisurl' => 'Redirect code (default: empty)',
'redirectifpathisurlinfo' => 'You only need to select one of these if you entered an URL as path<br/><strong class="text-danger">NOTE:</strong> Changes are only applied if the given path is an URL.', 'redirectifpathisurlinfo' => 'You only need to select one of these if you entered an URL as path<br/><strong class="text-danger">NOTE:</strong> Changes are only applied if the given path is an URL.',
'issubof' => 'This domain is a subdomain of another domain',
'issubofinfo' => 'You have to set this to the correct domain if you want to add a subdomain as full-domain (e.g. you want to add "www.domain.tld", you have to select "domain.tld" here)',
'nosubtomaindomain' => 'No subdomain of a full domain',
'ipandport_multi' => [ 'ipandport_multi' => [
'title' => 'IP address(es)', 'title' => 'IP address(es)',
'description' => 'Specify one or more IP address for the domain.<br /><br /><div class="text-danger">NOTE: IP addresses cannot be changed when the domain is configured as <strong>alias-domain</strong> of another domain.</div>', 'description' => 'Specify one or more IP address for the domain.<br /><br /><div class="text-danger">NOTE: IP addresses cannot be changed when the domain is configured as <strong>alias-domain</strong> of another domain.</div>',
@@ -769,6 +768,7 @@ return [
'openbasedirenabled' => 'Openbasedir restiction', 'openbasedirenabled' => 'Openbasedir restiction',
'hsts' => 'HSTS enabled', 'hsts' => 'HSTS enabled',
'aliasdomainid' => 'ID of alias domain', 'aliasdomainid' => 'ID of alias domain',
'nodomainsassignedbyadmin' => 'Your account has currently no (active) domains assigned to it. Please contact your administrator if you think this is wrong.',
], ],
'emails' => [ 'emails' => [
'description' => 'Here you can create and change your email addresses.<br />An account is like your letterbox in front of your house. If someone sends you an email, it will be dropped into the account.<br /><br />To download your emails use the following settings in your mailprogram: (The data in <i>italics</i> has to be changed to the equivalents you typed in!)<br />Hostname: <b><i>domainname</i></b><br />Username: <b><i>account name / e-mail address</i></b><br />password: <b><i>the password you\'ve chosen</i></b>', 'description' => 'Here you can create and change your email addresses.<br />An account is like your letterbox in front of your house. If someone sends you an email, it will be dropped into the account.<br /><br />To download your emails use the following settings in your mailprogram: (The data in <i>italics</i> has to be changed to the equivalents you typed in!)<br />Hostname: <b><i>domainname</i></b><br />Username: <b><i>account name / e-mail address</i></b><br />password: <b><i>the password you\'ve chosen</i></b>',
@@ -839,6 +839,7 @@ return [
'domainisaliasorothercustomer' => 'The selected alias domain is either itself an alias domain, has a different ip/port combination or belongs to another customer.', 'domainisaliasorothercustomer' => 'The selected alias domain is either itself an alias domain, has a different ip/port combination or belongs to another customer.',
'emailexistalready' => 'The email-address %s already exists.', 'emailexistalready' => 'The email-address %s already exists.',
'maindomainnonexist' => 'The main-domain %s does not exist.', 'maindomainnonexist' => 'The main-domain %s does not exist.',
'maindomaindeactivated' => 'The main-domain %s is deactivated.',
'destinationnonexist' => 'Please create your forwarder in the field \'Destination\'.', 'destinationnonexist' => 'Please create your forwarder in the field \'Destination\'.',
'destinationalreadyexistasmail' => 'The forwarder to %s already exists as active email-address.', 'destinationalreadyexistasmail' => 'The forwarder to %s already exists as active email-address.',
'destinationalreadyexist' => 'You have already defined a forwarder to "%s"', 'destinationalreadyexist' => 'You have already defined a forwarder to "%s"',
@@ -1001,6 +1002,7 @@ return [
'2fa_wrongcode' => 'The code entered is not valid', '2fa_wrongcode' => 'The code entered is not valid',
'gnupgextensionnotavailable' => 'The PHP GnuPG extension is not available. Unable to validate PGP Public Key', 'gnupgextensionnotavailable' => 'The PHP GnuPG extension is not available. Unable to validate PGP Public Key',
'invalidpgppublickey' => 'The PGP Public Key is not valid', 'invalidpgppublickey' => 'The PGP Public Key is not valid',
'invalid_validtime' => 'Valid time in seconds can only be between 10 and 120',
], ],
'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.',
@@ -1402,7 +1404,6 @@ Yours sincerely, your administrator',
'admin_quotas_reallyenforce' => 'Do you really want to enforce the default quota to all Users? This cannot be reverted!', 'admin_quotas_reallyenforce' => 'Do you really want to enforce the default quota to all Users? This cannot be reverted!',
'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.', 'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.',
'fpmsetting_reallydelete' => 'Do you really want to delete these php-fpm settings? All php configurations which use these settings currently will be changed to the default config.', 'fpmsetting_reallydelete' => 'Do you really want to delete these php-fpm settings? All php configurations which use these settings currently will be changed to the default config.',
'remove_subbutmain_domains' => 'Also remove domains which are added as full domains but which are subdomains of this domain?',
'customer_reallyunlock' => 'Do you really want to unlock customer %s?', 'customer_reallyunlock' => 'Do you really want to unlock customer %s?',
'admin_integritycheck_reallyfix' => 'Do you really want to try fixing all database integrity problems automatically?', 'admin_integritycheck_reallyfix' => 'Do you really want to try fixing all database integrity problems automatically?',
'plan_reallydelete' => 'Do you really want to delete the hosting plan %s?', 'plan_reallydelete' => 'Do you really want to delete the hosting plan %s?',
@@ -2140,12 +2141,8 @@ Yours sincerely, your administrator',
'description' => 'The content of this field will be included into this ip/port vHost container directly. You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/> Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!', 'description' => 'The content of this field will be included into this ip/port vHost container directly. You can use the following variables:<br/><code>{DOMAIN}</code>, <code>{DOCROOT}</code>, <code>{CUSTOMER}</code>, <code>{IP}</code>, <code>{PORT}</code>, <code>{SCHEME}</code>, <code>{FPMSOCKET}</code> (if applicable)<br/> Attention: The code won\'t be checked for any errors. If it contains errors, webserver might not start again!',
], ],
'includedefault_sslvhostconf' => 'Include non-SSL vHost-settings in SSL-vHost', 'includedefault_sslvhostconf' => 'Include non-SSL vHost-settings in SSL-vHost',
'apply_specialsettings_default' => [ 'apply_specialsettings_default' => 'Default value for "Apply specialsettings to all subdomains (*.example.com)" setting when editing a domain',
'title' => 'Default value for "Apply specialsettings to all subdomains (*.example.com)\' setting when editing a domain', 'apply_phpconfigs_default' => 'Default value for "Apply php-config to all subdomains" setting when editing a domain',
],
'apply_phpconfigs_default' => [
'title' => 'Default value for "Apply php-config to all subdomains:\' setting when editing a domain',
],
'awstats' => [ 'awstats' => [
'logformat' => [ 'logformat' => [
'title' => 'LogFormat setting', 'title' => 'LogFormat setting',

View File

@@ -731,9 +731,6 @@ return [
'aliasdomains' => 'Alias dominios', 'aliasdomains' => 'Alias dominios',
'redirectifpathisurl' => 'Código de redirección (por defecto: vacío)', 'redirectifpathisurl' => 'Código de redirección (por defecto: vacío)',
'redirectifpathisurlinfo' => 'Sólo tiene que seleccionar una de estas opciones si ha introducido una URL como ruta<br/><strong class="text-danger">NOTA:</strong> Los cambios sólo se aplican si la ruta indicada es una URL.', 'redirectifpathisurlinfo' => 'Sólo tiene que seleccionar una de estas opciones si ha introducido una URL como ruta<br/><strong class="text-danger">NOTA:</strong> Los cambios sólo se aplican si la ruta indicada es una URL.',
'issubof' => 'Este dominio es un subdominio de otro dominio',
'issubofinfo' => 'Si desea añadir un subdominio como dominio completo, deberá establecerlo en el dominio correcto (por ejemplo, si desea añadir "www.domain.tld", deberá seleccionar "dominio.tld").',
'nosubtomaindomain' => 'No es subdominio de un dominio completo',
'ipandport_multi' => [ 'ipandport_multi' => [
'title' => 'Direcciones IP', 'title' => 'Direcciones IP',
'description' => 'Especifique una o más direcciones IP para el dominio.<br/><br/><div class="text-danger">NOTA: Las direcciones IP no pueden cambiarse cuando el dominio está configurado como <strong>alias-dominio</strong> de otro dominio.</div>' 'description' => 'Especifique una o más direcciones IP para el dominio.<br/><br/><div class="text-danger">NOTA: Las direcciones IP no pueden cambiarse cuando el dominio está configurado como <strong>alias-dominio</strong> de otro dominio.</div>'
@@ -1390,7 +1387,6 @@ Atentamente, su administrador'
'admin_quotas_reallyenforce' => '¿Realmente desea aplicar la cuota por defecto a todos los usuarios? Esto no se puede revertir.', 'admin_quotas_reallyenforce' => '¿Realmente desea aplicar la cuota por defecto a todos los usuarios? Esto no se puede revertir.',
'phpsetting_reallydelete' => '¿Realmente desea eliminar esta configuración? Todos los dominios que usen esta configuración serán cambiados a la configuración por defecto.', 'phpsetting_reallydelete' => '¿Realmente desea eliminar esta configuración? Todos los dominios que usen esta configuración serán cambiados a la configuración por defecto.',
'fpmsetting_reallydelete' => '¿Realmente desea eliminar esta configuración de php-fpm? Todas las configuraciones de php que utilicen estos ajustes se cambiarán a la configuración por defecto.', 'fpmsetting_reallydelete' => '¿Realmente desea eliminar esta configuración de php-fpm? Todas las configuraciones de php que utilicen estos ajustes se cambiarán a la configuración por defecto.',
'remove_subbutmain_domains' => '¿Quitar también los dominios que se añaden como dominios completos pero que son subdominios de este dominio?',
'customer_reallyunlock' => '¿Realmente quieres desbloquear al cliente %s?', 'customer_reallyunlock' => '¿Realmente quieres desbloquear al cliente %s?',
'admin_integritycheck_reallyfix' => '¿Realmente quieres intentar arreglar todos los problemas de integridad de la base de datos automáticamente?', 'admin_integritycheck_reallyfix' => '¿Realmente quieres intentar arreglar todos los problemas de integridad de la base de datos automáticamente?',
'plan_reallydelete' => '¿De verdad quieres eliminar el plan de alojamiento %s?', 'plan_reallydelete' => '¿De verdad quieres eliminar el plan de alojamiento %s?',

View File

@@ -732,9 +732,6 @@ return [
'aliasdomains' => 'Alias domini', 'aliasdomains' => 'Alias domini',
'redirectifpathisurl' => 'Codice di redirezione (Predefinito: vuoto)', 'redirectifpathisurl' => 'Codice di redirezione (Predefinito: vuoto)',
'redirectifpathisurlinfo' => 'È necessario selezionare uno di questi se hai inserito un URL come percorso', 'redirectifpathisurlinfo' => 'È necessario selezionare uno di questi se hai inserito un URL come percorso',
'issubof' => 'Questo dominio è un sottodominio di un altro dominio',
'issubofinfo' => 'Devi impostare correttamente questo dominio se si desidera aggiungere un sottodominio come dominio completo (es. si vuole aggiungere "www.domain.tld", devi selezionare qui "domain.tld")',
'nosubtomaindomain' => 'No sottodominio di un dominio completo',
'ipandport_multi' => [ 'ipandport_multi' => [
'title' => 'Indirizzi IP', 'title' => 'Indirizzi IP',
'description' => 'Specifica uno o più indirizzi IP per il dominio.<br /><br /><div class="text-danger">NOTA: L\'indirizzo IP non può essere modificato quando il dominio è configurato come <strong>alias-domain</strong> di un altro dominio.</div>', 'description' => 'Specifica uno o più indirizzi IP per il dominio.<br /><br /><div class="text-danger">NOTA: L\'indirizzo IP non può essere modificato quando il dominio è configurato come <strong>alias-domain</strong> di un altro dominio.</div>',
@@ -1196,7 +1193,6 @@ Cordiali Saluti, Team Froxlor',
'admin_quotas_reallywipe' => 'Sei sicuro di voler cancellare tutti i limiti dalla tabella mail_users? Questa operazione non può essere annullata!', 'admin_quotas_reallywipe' => 'Sei sicuro di voler cancellare tutti i limiti dalla tabella mail_users? Questa operazione non può essere annullata!',
'admin_quotas_reallyenforce' => 'Sei sicuro di voler impostare il limite predefinito a tutti gli utenti? Questa operazione non può essere annullata!', 'admin_quotas_reallyenforce' => 'Sei sicuro di voler impostare il limite predefinito a tutti gli utenti? Questa operazione non può essere annullata!',
'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.', 'phpsetting_reallydelete' => 'Do you really want to delete these settings? All domains which use these settings currently will be changed to the default config.',
'remove_subbutmain_domains' => 'Rimuover anche i domini che sono stati aggiunti come domini completi, ma quali sono i sottodomini di questo dominio?',
'customer_reallyunlock' => 'Sei sicuro di voler sbloccare il cliente %s?', 'customer_reallyunlock' => 'Sei sicuro di voler sbloccare il cliente %s?',
'admin_customer_alsoremovemail' => 'Eliminare completamente i dati della posta elettronica dal filesystem??', 'admin_customer_alsoremovemail' => 'Eliminare completamente i dati della posta elettronica dal filesystem??',
'admin_customer_alsoremoveftphomedir' => 'Rimuovere anche la cartella homedir dell\'utente FTP?', 'admin_customer_alsoremoveftphomedir' => 'Rimuovere anche la cartella homedir dell\'utente FTP?',

View File

@@ -400,9 +400,6 @@ return [
'aliasdomains' => 'Alternatieve domeinnamen', 'aliasdomains' => 'Alternatieve domeinnamen',
'redirectifpathisurl' => 'Doorverwijzingscode (standaard: leegt)', 'redirectifpathisurl' => 'Doorverwijzingscode (standaard: leegt)',
'redirectifpathisurlinfo' => 'U dient deze alleen op te geven indien u een URL als pad hebt opgegeven', 'redirectifpathisurlinfo' => 'U dient deze alleen op te geven indien u een URL als pad hebt opgegeven',
'issubof' => 'Dit domein is een subdomein van een ander domein',
'issubofinfo' => 'U dient het correcte domein op te geven indien u een subdomein als volledig domein wilt (bijvoorbeeld als u "www.domain.tld" wilt gebruiken, dan geeft u hier "domain.tld")',
'nosubtomaindomain' => 'Geen subdomein van volledig domein',
], ],
'emails' => [ 'emails' => [
'description' => 'Hier kunt u e-mail adressen maken en wijzigen.<br />Een account is net als een brievenbus voor uw huis. Als iemand u mail stuurt wordt dit op uw account bezorgd.<br /><br />Om uw emails te downloaden moet u het volgende instellen in uw mailprogramma: (De <i>schuingedrukte</i> gegevens moeten gewijzigd worden in wat u ingegeven heeft!)<br />Servernaam: <b><i>Domeinnaam</i></b><br />Gebruikersnaam: <b><i>Account naam / E-mailadres</i></b><br />Wachtwoord: <b><i>het door u ingegeven wachtwoord</i></b>', 'description' => 'Hier kunt u e-mail adressen maken en wijzigen.<br />Een account is net als een brievenbus voor uw huis. Als iemand u mail stuurt wordt dit op uw account bezorgd.<br /><br />Om uw emails te downloaden moet u het volgende instellen in uw mailprogramma: (De <i>schuingedrukte</i> gegevens moeten gewijzigd worden in wat u ingegeven heeft!)<br />Servernaam: <b><i>Domeinnaam</i></b><br />Gebruikersnaam: <b><i>Account naam / E-mailadres</i></b><br />Wachtwoord: <b><i>het door u ingegeven wachtwoord</i></b>',
@@ -741,7 +738,6 @@ Met vriendelijke groet, uw beheerder',
'admin_quotas_reallywipe' => 'Weet u zeker dat u alle quota wilt verwijderen? Dit is niet terug te draaien!', 'admin_quotas_reallywipe' => 'Weet u zeker dat u alle quota wilt verwijderen? Dit is niet terug te draaien!',
'admin_quotas_reallyenforce' => 'Weet u zeker dat u quota wilt afdwingen? Dit is niet terug te draaien!', 'admin_quotas_reallyenforce' => 'Weet u zeker dat u quota wilt afdwingen? Dit is niet terug te draaien!',
'phpsetting_reallydelete' => 'Weet u zeker dat u deze instellingen wilt verwijderen? Alle domeinen die deze configuratie gebruiken zullen terugvallen op de standaardinstellingen.', 'phpsetting_reallydelete' => 'Weet u zeker dat u deze instellingen wilt verwijderen? Alle domeinen die deze configuratie gebruiken zullen terugvallen op de standaardinstellingen.',
'remove_subbutmain_domains' => 'Verwijder ook domeinen die als volledige domeinen zijn opgegeven maar een subdomein zijn van dit domein?',
'customer_reallyunlock' => 'Weet u zeker dat u klant %s? wilt ontgrendelen', 'customer_reallyunlock' => 'Weet u zeker dat u klant %s? wilt ontgrendelen',
], ],
'serversettings' => [ 'serversettings' => [

View File

@@ -3,7 +3,7 @@
{% block content %} {% block content %}
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<form class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded"> <form action="{{ formaction }}" class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/> <img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
<div class="card shadow"> <div class="card shadow">
@@ -38,8 +38,6 @@
</div> </div>
<div class="card-body d-grid gap-2"> <div class="card-body d-grid gap-2">
<input type="hidden" name="action" value="{{ action }}"/>
<input type="hidden" name="send" value="send"/>
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button> <button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
</div> </div>

View File

@@ -39,9 +39,6 @@
</div> </div>
<div class="card-body d-grid gap-2"> <div class="card-body d-grid gap-2">
<input type="hidden" name="script" value="{{ lastscript }}"/>
<input type="hidden" name="qrystr" value="{{ lastqrystr|raw }}"/>
<input type="hidden" name="send" value="send"/>
<button class="btn btn-primary rounded-top-0" type="submit" name="dologin">{{ lng('login.login') }}</button> <button class="btn btn-primary rounded-top-0" type="submit" name="dologin">{{ lng('login.login') }}</button>
</div> </div>

View File

@@ -30,8 +30,6 @@
</div> </div>
<div class="card-body d-grid gap-2"> <div class="card-body d-grid gap-2">
<input type="hidden" name="action" value="resetpwd"/>
<input type="hidden" name="send" value="send"/>
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button> <button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
</div> </div>

View File

@@ -111,7 +111,7 @@
<select class="form-select" name="searchfield" id="searchfield"> <select class="form-select" name="searchfield" id="searchfield">
{% for key, column in listing.available_columns %} {% for key, column in listing.available_columns %}
{% if column.searchable is not defined or (column.searchable is defined and column.searchable == true) %} {% if column.searchable is not defined or (column.searchable is defined and column.searchable == true) %}
<option value="{{ key }}">{{ column.label }}</option> <option value="{{ key }}" {% if column.isdefaultsearchfield is defined and column.isdefaultsearchfield == true %}selected{% endif %}>{{ column.label }}</option>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</select> </select>

View File

@@ -1,2 +1,2 @@
{% import "Froxlor/form/form.html.twig" as form %} {% import "Froxlor/form/form.html.twig" as form %}
{{ form.form(formdata, formaction|default('#'), formdata.title, editid|default(''), true, idprefix|default('')) }} {{ form.form(formdata, formaction|default('#'), formdata.title, editid|default(''), nosubmit|default(true), idprefix|default('')) }}

View File

@@ -35,7 +35,7 @@
{% for link in actions_links %} {% for link in actions_links %}
<a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}"> <a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}">
<i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i> <i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i>
<span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span> {% if link.label is defined and link.label is not empty %}<span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span>{% endif %}
</a> </a>
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>froxlor - Deactivated page</title>
<style>
:root{--primary:#1872a2;--fonts:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}
body{display:flex;flex-direction:column;background:#f8f9fa;color:#4b5563;align-items:center;justify-content:center;font-family:var(--fonts)}
main{background:#fff;margin:10% auto 12px;max-width:540px;padding:2rem;box-shadow:4px 8px 16px 0 rgba(0,0,0,.07);border-radius:.375rem}
main h2{margin:0}
main p{margin-bottom:1.5rem}
main p:last-child{margin-bottom:0}
main ul{list-style:none;margin-left:-40px}
main li{display:flex;align-items:center;margin-bottom:1rem}
main .icon{min-width:24px;width:24px;stroke:var(--primary);margin-right:.75rem}
code{background:#eee;padding:.1rem .25rem;border-radius:4px;color:rgba(0,0,0,.75)}
hr{margin:2rem 0;border:none;border-bottom:solid 1px rgba(0,0,0,.1)}
a,a:active,a:visited{color:var(--primary);text-decoration:none}
a:hover{text-decoration:underline}
footer{display:flex;align-items:center;margin-top:.5rem}
footer .logo{margin-right:.35rem}
@media (prefers-color-scheme: dark) {
:root{--primary:#29a2d6}
body{background:#212529;color:#f8f9fa}
main{background:#343a40}
hr{border-color:rgba(0,0,0,.2)}
}
</style>
</head>
<body>
<main>
<h2>Domain deactivated</h2>
<p>
This domain is managed using the <a href="https://froxlor.org" rel="external">froxlor Server Management Panel</a>.
If you see this page, this domain has been deactivated by an administrator.
</p>
<ul>
<li>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
</svg>
<span>Please ask your provider/hoster if you think this is not correct</span>
</li>
</ul>
</main>
<footer>
<img class="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAAAQCAYAAAC1MDndAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAy1JREFUeNrs1muIVlUUBuBnvqZkoswudKErZFOG0kWt6WpN0wWCDMOsgX4UmVS/KovKhIKYLhRBJvV1o/5VlJCmYEmUFYijaBYhSRQVBVE0ZnRBJ/vzfnA4nDN+wTh/pgWHc/baa6+z97v3et/d0Ww24SQ8ij5MNL7tN6zBvdjWiZOxDpP8b3JA5qAXPQ0MtAnOm9jZBvqbsQHbsGsMFrQLv+KPUc47CQONlFXZPsNfJd/buBrDFfHDWIDDcAZmohsPjwFAD+EQnL8Xcl/WqOGc+/FJyXcxVuHJivjX8Hx2cwGeweO4fIxOkL10Wg9o1HSsxUcl30V5P1JxnFuxp+M53I67cQ5mYwZexj2YlnarJBdl3BE4FfPxdfofSOwNhX+uwgXxf1Uz/024DpNxFHrwRIEiPs74XnyIq3A8Xion6qxIvj0TX1vyb8x7CJ/jrELfjrx/zgmS0p2bCWzHzejAQTgmO35J+OpInIcv8GLKeROuzcI24lAsRH/yzcGJFfMfDIB/B/ipeD8bNojX8W1hPR+gCxPwTzsAdaGRgTeGrAZLJXd4zc59l1Jr2dzC91S8g+PSXhFwGgGxO4s6Bd9gaQRkAHdhSQHso/FCzRweS56ezLmB1bgCb+DBCg67rwaLSud+uDAAvVLRfw1OqCO18JEKbusvgNMqA/F153sCpgegT+O7Iwt8NwrZwKsh5irbUuDMFoXMquhv2eKRSKiOg54OUFXSt3SEfPvi4Dz77IEAW/8uH+vdpf6dkXGF+F9GyNtRkXe4or8tqwNoWrigbDeFTEfDZhbKckuBy9blu0XkC1Pi++PK+Objy5q80/NeXSDl9wrgnDkaAIlyzCj5zh1FCb00JL07Jd2LKfgBx+LWXE6XJP6pcMiUiMg8/FmRd3GEYHNiZ+H6wgZP/i+T7MzPJo5wkjYU2t01cWfjp6hH2WZn0adVbM7KALAS3weYftwZIVgTIHtxS8YtD6kOYX2Uqi8iIKCsD6CDhXnNi+iI+vXhwD3g83tHs9l8K5JZZbfh2UL7x0jyeLFljVzUhtoc0DWOwBnCoga25s6wrHDhG8+2I1j0YOu/AwBUU7aBHvM/ZwAAAABJRU5ErkJggg==" alt="froxlor"/>
<small>&copy; 2009-<span id="year"></span> by <a href="https://froxlor.org" rel="external">the froxlor team</a></small>
</footer>
<script>
document.getElementById("year").innerHTML = new Date().getFullYear();
</script>
</body>
</html>

View File

@@ -385,6 +385,26 @@ class DomainsTest extends TestCase
* *
* @depends testAdminDomainsMove * @depends testAdminDomainsMove
*/ */
public function testAdminDomainsDuplicate()
{
global $admin_userdata;
$data = [
'domainname' => 'test.local',
'domain' => 'test.duplicate.local',
'description' => 'duplicated domain'
];
$json_result = Domains::getLocal($admin_userdata, $data)->duplicate();
$result = json_decode($json_result, true)['data'];
$this->assertEquals('/var/customers/webs/test3/test.duplicate.local/', $result['documentroot']);
$this->assertEquals(1, $result['email_only']);
$this->assertEquals('test.duplicate.local', $result['domain']);
$this->assertEquals('duplicated domain', $result['description']);
}
/**
*
* @depends testAdminDomainsDuplicate
*/
public function testAdminDomainsDelete() public function testAdminDomainsDelete()
{ {
global $admin_userdata; global $admin_userdata;