Compare commits

...

43 Commits

Author SHA1 Message Date
Michael Kaufmann
50f2047da3 set version to 0.10.17 for upcoming maintenance release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-15 20:35:25 +02:00
Michael Kaufmann
ecb9470b65 fix including of language-strings in reports-cron, fixes #836
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-15 15:16:24 +02:00
Michael Kaufmann
6d90b5ba80 remove leftover GROUP BY from testing
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-15 09:56:24 +02:00
Michael Kaufmann
eb3590dc34 add unique-key domainid to domain_ssl_settings table
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-15 09:49:53 +02:00
Michael Kaufmann
bddf9b496c enable internal api-call to bypass customer_hide_options check in certain situations where it is needed, fixes #803
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-15 09:35:20 +02:00
Michael Kaufmann
edc702dafa check for required min version of php every time (frontend and cron), fixes #833
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-08 10:03:01 +02:00
Michael Kaufmann
85dfc1030a Merge pull request #832 from RipClaw2971/bugfix
Certificate file cannot be read to database
2020-05-04 16:11:42 +02:00
Andreas Grundler
c0dd432916 Certificate file cannot be read to database if the domain contains capital letters. 2020-05-04 12:08:20 +02:00
Michael Kaufmann
b3db4dd887 set version to 0.10.16 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-05-01 16:27:23 +02:00
Michael Kaufmann
14413a3e8d try to fix travis irc notifications again
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-30 20:10:52 +02:00
Michael Kaufmann
a02a081c6b try to fix travis irc notifications
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-30 20:03:54 +02:00
Michael Kaufmann
43070e4808 remove possible trailing slash of apache DocumentRoot diretives as sugggested by the httpd-docs, thx to jonbert
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-30 19:49:20 +02:00
Michael Kaufmann
98c636c282 let send-to-alternative-email be optional if no address is given instead of displaying error that the email address is invalid; fixes #829
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-30 13:03:19 +02:00
Michael Kaufmann
8dace6eca5 remove special characters from name when generating extrausers file
add froxlor hostname to 2fa-qrcode; refs #814

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-28 07:59:37 +02:00
Michael Kaufmann
78fc4f84b2 add optional dns validation for let's encrypt activated domains; fixes #817
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-14 10:28:33 +02:00
Michael Kaufmann
9018404faa Double check whether installation of acme.sh worked when not installed yet and do not continue if not; fixes #823
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-14 08:10:36 +02:00
Michael Kaufmann
8bdd843bd9 fix renew of froxlors own letsencrypt certificate; fix only variables should be passed by reference in BackupCron; fix possible notice or double inclusion of language file in ReportsCron
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-08 09:07:48 +02:00
Michael Kaufmann
0d35f5cb29 restructure acmesh implementation and let acme.sh take care of renewing the certificates itself; fixes #792, fixes #816
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-04-04 18:04:39 +02:00
Michael Kaufmann
6815c1c20b Merge pull request #821 from chrschn/master
Fix renewal of ECC/ECDSA certificates.
2020-03-29 23:52:34 +02:00
Christian Schneider
048e6c13ae Fix renewal of ECC/ECDSA certificates.
The ACME v2 implementation uses separate directoies for ECC and on-ECC
certificates. The renew command for a domain checks if an ECC directory
exists (having a "_ecc" suffix) and refuses the command unless the
"--ecc" flag was specified.

Confusingly, this flag is only required to *renew* an ECC certificate,
but not to issue it.

This fixes https://github.com/Froxlor/Froxlor/issues/820.
2020-03-29 22:36:26 +02:00
Michael Kaufmann
aedb829a74 Merge pull request #819 from HBerni/hberni-p0002
fixed parsing due to changes in dovecots default mail_log_prefix
2020-03-21 17:36:00 +01:00
HBerni
4745581720 fixed parsing due to changes in dovecots default mail_log_prefix
regex supports old and new default format now
(see https://wiki2.dovecot.org/Upgrading/2.3)
2020-03-21 16:39:01 +01:00
Michael Kaufmann
489ccbe07a fix removing ip address if ip is set as system-ipaddress but there are other entries of that ip with a different port
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-12 07:01:26 +01:00
Michael Kaufmann
a46e7a3bc4 set correct umask to create user config in installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-10 07:48:35 +01:00
Michael Kaufmann
a4431e25d3 remove ssl-certificates connected to domains that are being deleted when deleting a customer; fixes #818
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-10 07:36:22 +01:00
Michael Kaufmann
1fe9f1e9d6 fix language strings for cron cmdline setting and fallback to php binary if no setting is found
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-08 10:30:33 +01:00
Michael Kaufmann
13767df562 set version to 0.10.15 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-08 07:39:39 +01:00
Michael Kaufmann
02c5f80854 correct chmod value for userdata.inc.php file to be written on installation; fixes #815
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-08 07:35:19 +01:00
Michael Kaufmann
d7550ae58a fix deactivated check in api
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-06 22:10:01 +01:00
Michael Kaufmann
cf2c7fa31c deny api access to deactivated users with valid api-key
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-06 22:03:41 +01:00
Michael Kaufmann
32b6285589 set version to 0.10.14 for upcoming release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-06 20:47:20 +01:00
Michael Kaufmann
7e361274c5 forgot one escapeshellarg() and enhanced security on userdata.inc.php creation when installing
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-06 20:44:17 +01:00
Michael Kaufmann
62ce21c9ec secure shell-execution of mysqldump on installation if given database-name exists
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-04 19:35:57 +01:00
Michael Kaufmann
6b09720ef8 use unpredictable tmpfile-name in installation if lib/userdata.inc.php cannot be written due to permission
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-04 19:34:18 +01:00
Michael Kaufmann
8807ae7dad allow private ip ranges in ips-and-ports as some configurations require that; fixes #802
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-02 09:51:44 +01:00
Michael Kaufmann
5f3f208534 remove superfluous comma in sql query which causes invalid sql
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-03-01 09:16:47 +01:00
Michael Kaufmann
f11ceacf89 store ace-string of domain besides idn-converted string to have correct sorting in the frontend; fixes #809
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-29 09:50:29 +01:00
Michael Kaufmann
26e43077c2 make customer firstname,name,company and customer-no available for all templates; fixes #808
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-29 08:16:55 +01:00
Michael Kaufmann
d6c8b92523 add Froxlor.integrityCheck() API call to externally run integrity/consistency check, fixes #801
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-15 07:30:56 +01:00
Michael Kaufmann
03450dcfa2 fix listing of customer email addresses if 'domain' section is hidden via settings, fixes #803
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-15 07:25:23 +01:00
Michael Kaufmann
f39aab6f32 disable sslsessiontickets-option in domain-add/edit if globally disabled in the settings
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-15 07:11:28 +01:00
Michael Kaufmann
7f999302fa do not require enabled vhost-container for froxlor-vhost to change sslsessiontickets-setting
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-14 19:26:06 +01:00
Michael Kaufmann
8294985588 require set password complexity for admins too when resetting password; display correct error message if password complexity is not satisfied
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2020-02-08 10:03:41 +01:00
54 changed files with 1038 additions and 495 deletions

View File

@@ -58,7 +58,7 @@ after_success:
- bash <(curl -s https://codecov.io/bash) -f "build/logs/clover.xml" - bash <(curl -s https://codecov.io/bash) -f "build/logs/clover.xml"
notifications: notifications:
irc: "irc.freenode.org#froxlor" irc: "chat.freenode.net#froxlor"
webhooks: webhooks:
urls: urls:
- https://webhooks.gitter.im/e/bdf91d1c3f745e51f796 - https://webhooks.gitter.im/e/bdf91d1c3f745e51f796

View File

@@ -38,7 +38,7 @@ if (AREA == 'admin') {
} }
$success_message = ""; $success_message = "";
$tfa = new \Froxlor\FroxlorTwoFactorAuth('Froxlor'); $tfa = new \Froxlor\FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
// do the delete and then just show a success-message // do the delete and then just show a success-message
if ($action == 'delete') { if ($action == 'delete') {

View File

@@ -122,10 +122,7 @@ return array(
'type' => 'bool', 'type' => 'bool',
'default' => true, 'default' => true,
'save_method' => 'storeSettingField', 'save_method' => 'storeSettingField',
'visible' => \Froxlor\Settings::Get('system.use_ssl') && (\Froxlor\Settings::Get('system.webserver') == "nginx" || (\Froxlor\Settings::Get('system.webserver') == "apache2" && \Froxlor\Settings::Get('system.apache24') == 1)) && call_user_func(array( 'visible' => \Froxlor\Settings::Get('system.use_ssl') && (\Froxlor\Settings::Get('system.webserver') == "nginx" || (\Froxlor\Settings::Get('system.webserver') == "apache2" && \Froxlor\Settings::Get('system.apache24') == 1))
'\Froxlor\Settings\FroxlorVhostSettings',
'hasVhostContainerEnabled'
), true)
), ),
'system_leenabled' => array( 'system_leenabled' => array(
'label' => $lng['serversettings']['leenabled'], 'label' => $lng['serversettings']['leenabled'],
@@ -217,11 +214,11 @@ return array(
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
), ),
'system_disable_le_selfcheck' => array( 'system_disable_le_selfcheck' => array(
'label' => $lng['serversettings']['disable_le_selfcheck'], 'label' => $lng['serversettings']['le_domain_dnscheck'],
'settinggroup' => 'system', 'settinggroup' => 'system',
'varname' => 'disable_le_selfcheck', 'varname' => 'le_domain_dnscheck',
'type' => 'bool', 'type' => 'bool',
'default' => false, 'default' => true,
'save_method' => 'storeSettingField' 'save_method' => 'storeSettingField'
) )
) )

View File

@@ -39,7 +39,7 @@ if ($page == 'domains' || $page == 'overview') {
$log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "viewed admin_domains"); $log->logAction(\Froxlor\FroxlorLogger::ADM_ACTION, LOG_NOTICE, "viewed admin_domains");
$fields = array( $fields = array(
'd.domain' => $lng['domains']['domainname'], 'd.domain_ace' => $lng['domains']['domainname'],
'c.name' => $lng['customer']['name'], 'c.name' => $lng['customer']['name'],
'c.firstname' => $lng['customer']['firstname'], 'c.firstname' => $lng['customer']['firstname'],
'c.company' => $lng['customer']['company'], 'c.company' => $lng['customer']['company'],

View File

@@ -160,5 +160,14 @@ if ($page == 'ipsandports' || $page == 'overview') {
eval("echo \"" . \Froxlor\UI\Template::getTemplate("ipsandports/ipsandports_edit") . "\";"); eval("echo \"" . \Froxlor\UI\Template::getTemplate("ipsandports/ipsandports_edit") . "\";");
} }
} }
} elseif ($action == 'jqCheckIP') {
$ip = $_POST['ip'] ?? "";
if ((filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE) == false) {
// returns notice if private network detected so we can display it
echo json_encode($lng['admin']['ipsandports']['ipnote']);
} else {
echo 0;
}
exit();
} }
} }

View File

@@ -42,7 +42,7 @@ if ($page == 'overview') {
if ($action == '') { if ($action == '') {
$log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_domains::domains"); $log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_domains::domains");
$fields = array( $fields = array(
'd.domain' => $lng['domains']['domainname'], 'd.domain_ace' => $lng['domains']['domainname'],
'd.aliasdomain' => $lng['domains']['aliasdomain'] 'd.aliasdomain' => $lng['domains']['aliasdomain']
); );
try { try {

View File

@@ -19,7 +19,6 @@
define('AREA', 'customer'); define('AREA', 'customer');
require './lib/init.php'; require './lib/init.php';
use Froxlor\Api\Commands\SubDomains;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\Api\Commands\Emails as Emails; use Froxlor\Api\Commands\Emails as Emails;
@@ -44,7 +43,7 @@ if ($page == 'overview') {
if ($action == '') { if ($action == '') {
$log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails"); $log->logAction(\Froxlor\FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed customer_email::emails");
$fields = array( $fields = array(
'd.domain' => $lng['domains']['domainname'], 'd.domain_ace' => $lng['domains']['domainname'],
'm.email_full' => $lng['emails']['emailaddress'], 'm.email_full' => $lng['emails']['emailaddress'],
'm.destination' => $lng['emails']['forwarders'] 'm.destination' => $lng['emails']['forwarders']
); );
@@ -76,7 +75,7 @@ if ($page == 'overview') {
$emails[$row['domain']][$row['email_full']] = $row; $emails[$row['domain']][$row['email_full']] = $row;
} }
if ($paging->sortfield == 'd.domain' && $paging->sortorder == 'desc') { if ($paging->sortfield == 'd.domain_ace' && $paging->sortorder == 'desc') {
krsort($emails); krsort($emails);
} else { } else {
ksort($emails); ksort($emails);
@@ -129,16 +128,15 @@ if ($page == 'overview') {
} }
} }
$json_result = SubDomains::getLocal($userinfo, [ $result_stmt = Database::prepare("
'sql_search' => [ SELECT COUNT(`id`) as emaildomains
'd.isemaildomain' => [ FROM `" . TABLE_PANEL_DOMAINS . "`
'value' => 1, WHERE `customerid`= :cid AND `isemaildomain` = '1'
'op' => '=' ");
] $result = Database::pexecute_first($result_stmt, array(
] "cid" => $userinfo['customerid']
])->listing(); ));
$result = json_decode($json_result, true)['data']; $emaildomains_count = $result['emaildomains'];
$emaildomains_count = $result['count'];
eval("echo \"" . \Froxlor\UI\Template::getTemplate("email/emails") . "\";"); eval("echo \"" . \Froxlor\UI\Template::getTemplate("email/emails") . "\";");
} elseif ($action == 'delete' && $id != 0) { } elseif ($action == 'delete' && $id != 0) {
@@ -196,7 +194,7 @@ if ($page == 'overview') {
$result_stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "` $result_stmt = Database::prepare("SELECT `id`, `domain`, `customerid` FROM `" . TABLE_PANEL_DOMAINS . "`
WHERE `customerid`= :cid WHERE `customerid`= :cid
AND `isemaildomain`='1' AND `isemaildomain`='1'
ORDER BY `domain` ASC"); ORDER BY `domain_ace` ASC");
Database::pexecute($result_stmt, array( Database::pexecute($result_stmt, array(
"cid" => $userinfo['customerid'] "cid" => $userinfo['customerid']
)); ));

View File

@@ -393,7 +393,7 @@ if ($action == 'forgotpwd') {
if (isset($_POST['send']) && $_POST['send'] == 'send') { if (isset($_POST['send']) && $_POST['send'] == 'send') {
$loginname = \Froxlor\Validate\Validate::validate($_POST['loginname'], 'loginname'); $loginname = \Froxlor\Validate\Validate::validate($_POST['loginname'], 'loginname');
$email = \Froxlor\Validate\Validate::validateEmail($_POST['loginemail'], 'email'); $email = \Froxlor\Validate\Validate::validateEmail($_POST['loginemail'], 'email');
$result_stmt = Database::prepare("SELECT `adminid`, `customerid`, `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 . "`
WHERE `loginname`= :loginname WHERE `loginname`= :loginname
AND `email`= :email"); AND `email`= :email");
Database::pexecute($result_stmt, array( Database::pexecute($result_stmt, array(
@@ -481,6 +481,10 @@ if ($action == 'forgotpwd') {
$replace_arr = array( $replace_arr = array(
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($user), 'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($user),
'NAME' => $user['name'],
'FIRSTNAME' => $user['firstname'] ?? "",
'COMPANY' => $user['company'] ?? "",
'CUSTOMER_NO' => $user['customernumber'] ?? 0,
'USERNAME' => $loginname, 'USERNAME' => $loginname,
'LINK' => $activationlink 'LINK' => $activationlink
); );
@@ -598,21 +602,18 @@ if ($action == 'resetpwd') {
)); ));
if ($result !== false) { if ($result !== false) {
if ($result['admin'] == 1) { try {
$new_password = \Froxlor\Validate\Validate::validate($_POST['new_password'], 'new password'); $new_password = \Froxlor\System\Crypt::validatePassword($_POST['new_password'], true);
$new_password_confirm = \Froxlor\Validate\Validate::validate($_POST['new_password_confirm'], 'new password confirm'); $new_password_confirm = \Froxlor\System\Crypt::validatePassword($_POST['new_password_confirm'], true);
} else { } catch (Exception $e) {
$new_password = \Froxlor\System\Crypt::validatePassword($_POST['new_password'], 'new password'); $message = $e->getMessage();
$new_password_confirm = \Froxlor\System\Crypt::validatePassword($_POST['new_password_confirm'], 'new password confirm');
} }
if ($new_password == '') { if (empty($message) && (empty($new_password) || $new_password != $new_password_confirm)) {
$message = $new_password; $message = $lng['error']['newpasswordconfirmerror'];
} elseif ($new_password_confirm == '') { }
$message = $new_password_confirm;
} elseif ($new_password != $new_password_confirm) { if (empty($message)) {
$message = $new_password . " != " . $new_password_confirm;
} else {
// Update user password // Update user password
if ($result['admin'] == 1) { if ($result['admin'] == 1) {
$stmt = Database::prepare("UPDATE `" . TABLE_PANEL_ADMINS . "` $stmt = Database::prepare("UPDATE `" . TABLE_PANEL_ADMINS . "`

View File

@@ -84,7 +84,7 @@ CREATE TABLE `panel_activation` (
`creation` int(11) unsigned NOT NULL default '0', `creation` int(11) unsigned NOT NULL default '0',
`activationcode` varchar(50) default NULL, `activationcode` varchar(50) default NULL,
PRIMARY KEY (id) PRIMARY KEY (id)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
DROP TABLE IF EXISTS `panel_admins`; DROP TABLE IF EXISTS `panel_admins`;
@@ -224,6 +224,7 @@ DROP TABLE IF EXISTS `panel_domains`;
CREATE TABLE `panel_domains` ( CREATE TABLE `panel_domains` (
`id` int(11) unsigned NOT NULL auto_increment, `id` int(11) unsigned NOT NULL auto_increment,
`domain` varchar(255) NOT NULL default '', `domain` varchar(255) NOT NULL default '',
`domain_ace` varchar(255) NOT NULL default '',
`adminid` int(11) unsigned NOT NULL default '0', `adminid` int(11) unsigned NOT NULL default '0',
`customerid` int(11) unsigned NOT NULL default '0', `customerid` int(11) unsigned NOT NULL default '0',
`aliasdomain` int(11) unsigned NULL, `aliasdomain` int(11) unsigned NULL,
@@ -653,7 +654,7 @@ opcache.interned_strings_buffer'),
('system', 'leregistered', '0'), ('system', 'leregistered', '0'),
('system', 'leaccount', ''), ('system', 'leaccount', ''),
('system', 'nssextrausers', '0'), ('system', 'nssextrausers', '0'),
('system', 'disable_le_selfcheck', '0'), ('system', 'le_domain_dnscheck', '1'),
('system', 'ssl_protocols', 'TLSv1,TLSv1.2'), ('system', 'ssl_protocols', 'TLSv1,TLSv1.2'),
('system', 'tlsv13_cipher_list', ''), ('system', 'tlsv13_cipher_list', ''),
('system', 'honorcipherorder', '0'), ('system', 'honorcipherorder', '0'),
@@ -703,8 +704,8 @@ opcache.interned_strings_buffer'),
('panel', 'password_special_char', '!?<>§$%+#=@'), ('panel', 'password_special_char', '!?<>§$%+#=@'),
('panel', 'customer_hide_options', ''), ('panel', 'customer_hide_options', ''),
('panel', 'is_configured', '0'), ('panel', 'is_configured', '0'),
('panel', 'version', '0.10.13'), ('panel', 'version', '0.10.17'),
('panel', 'db_version', '201912313'); ('panel', 'db_version', '202005150');
DROP TABLE IF EXISTS `panel_tasks`; DROP TABLE IF EXISTS `panel_tasks`;
@@ -996,7 +997,8 @@ CREATE TABLE IF NOT EXISTS `domain_ssl_settings` (
`ssl_csr_file` mediumtext, `ssl_csr_file` mediumtext,
`ssl_fullchain_file` mediumtext, `ssl_fullchain_file` mediumtext,
`expirationdate` datetime DEFAULT NULL, `expirationdate` datetime DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
UNIQUE KEY (`domainid`)
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci; ) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;

View File

@@ -332,22 +332,29 @@ class FroxlorInstall
$userdata .= "?>"; $userdata .= "?>";
// test if we can store the userdata.inc.php in ../lib // test if we can store the userdata.inc.php in ../lib
$umask = @umask(077);
$userdata_file = dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php'; $userdata_file = dirname(dirname(dirname(__FILE__))) . '/lib/userdata.inc.php';
if ($fp = @fopen($userdata_file, 'w')) { if (@touch($userdata_file) && @is_writable($userdata_file)) {
$result = @fputs($fp, $userdata, strlen($userdata)); $fp = @fopen($userdata_file, 'w');
@fputs($fp, $userdata, strlen($userdata));
@fclose($fp); @fclose($fp);
$content .= $this->_status_message('green', 'OK'); $content .= $this->_status_message('green', 'OK');
chmod($userdata_file, 0440);
} elseif ($fp = @fopen('/tmp/userdata.inc.php', 'w')) {
$result = @fputs($fp, $userdata, strlen($userdata));
@fclose($fp);
$content .= $this->_status_message('orange', $this->_lng['install']['creating_configfile_temp']);
chmod('/tmp/userdata.inc.php', 0440);
} else { } else {
$content .= $this->_status_message('red', $this->_lng['install']['creating_configfile_failed']); @unlink($userdata_file);
$escpduserdata = nl2br(htmlspecialchars($userdata)); // try creating it in a temporary file
eval("\$content .= \"" . $this->_getTemplate("textarea") . "\";"); $temp_file = @tempnam(sys_get_temp_dir(), 'fx');
if ($temp_file) {
$fp = @fopen($temp_file, 'w');
@fputs($fp, $userdata, strlen($userdata));
@fclose($fp);
$content .= $this->_status_message('orange', sprintf($this->_lng['install']['creating_configfile_temp'], $temp_file));
} else {
$content .= $this->_status_message('red', $this->_lng['install']['creating_configfile_failed']);
$escpduserdata = nl2br(htmlspecialchars($userdata));
eval("\$content .= \"" . $this->_getTemplate("textarea") . "\";");
}
} }
@umask($umask);
return $content; return $content;
} }
@@ -563,7 +570,7 @@ class FroxlorInstall
for ($i = 0; $i < sizeof($sql_query); $i ++) { for ($i = 0; $i < sizeof($sql_query); $i ++) {
if (trim($sql_query[$i]) != '') { if (trim($sql_query[$i]) != '') {
try { try {
$result = $db->query($sql_query[$i]); $db->query($sql_query[$i]);
} catch (\PDOException $e) { } catch (\PDOException $e) {
$content .= $this->_status_message('red', $e->getMessage()); $content .= $this->_status_message('red', $e->getMessage());
$fatal_fail = true; $fatal_fail = true;
@@ -730,7 +737,7 @@ class FroxlorInstall
} }
if ($do_backup) { if ($do_backup) {
$command = $mysql_dump . " " . $this->_data['mysql_database'] . " -u " . $this->_data['mysql_root_user'] . " --password='" . $this->_data['mysql_root_pass'] . "' --result-file=" . $filename; $command = $mysql_dump . " " . escapeshellarg($this->_data['mysql_database']) . " -u " . escapeshellarg($this->_data['mysql_root_user']) . " --password='" . escapeshellarg($this->_data['mysql_root_pass']) . "' --result-file=" . $filename;
$output = exec($command); $output = exec($command);
if (stristr($output, "error")) { if (stristr($output, "error")) {
$content .= $this->_status_message('red', $this->_lng['install']['backup_failed']); $content .= $this->_status_message('red', $this->_lng['install']['backup_failed']);

View File

@@ -86,7 +86,7 @@ $lng['install']['changing_data'] = 'Adjusting settings...';
$lng['install']['creating_entries'] = 'Inserting new values...'; $lng['install']['creating_entries'] = 'Inserting new values...';
$lng['install']['adding_admin_user'] = 'Creating admin-account...'; $lng['install']['adding_admin_user'] = 'Creating admin-account...';
$lng['install']['creating_configfile'] = 'Creating configfile...'; $lng['install']['creating_configfile'] = 'Creating configfile...';
$lng['install']['creating_configfile_temp'] = 'File was saved in /tmp/userdata.inc.php, please move to ' . dirname(dirname(__DIR__)) . '/lib/.'; $lng['install']['creating_configfile_temp'] = 'File was saved in %s, please move to ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php';
$lng['install']['creating_configfile_failed'] = 'Could not create ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php, please create it manually with the following content:'; $lng['install']['creating_configfile_failed'] = 'Could not create ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php, please create it manually with the following content:';
$lng['install']['froxlor_succ_installed'] = 'Froxlor was installed successfully.'; $lng['install']['froxlor_succ_installed'] = 'Froxlor was installed successfully.';

View File

@@ -76,7 +76,7 @@ $lng['install']['changing_data'] = 'Ajustement des paramètres...';
$lng['install']['creating_entries'] = 'Insertion des nouvelles valeurs...'; $lng['install']['creating_entries'] = 'Insertion des nouvelles valeurs...';
$lng['install']['adding_admin_user'] = 'Création du compte administrateur...'; $lng['install']['adding_admin_user'] = 'Création du compte administrateur...';
$lng['install']['creating_configfile'] = 'Création du fichier de configuration...'; $lng['install']['creating_configfile'] = 'Création du fichier de configuration...';
$lng['install']['creating_configfile_temp'] = 'Le fichier a été enregistré dans /tmp/userdata.inc.php, merci de le déplacer dans ' . dirname(dirname(__DIR__)) . '/lib/.'; $lng['install']['creating_configfile_temp'] = 'Le fichier a été enregistré dans %s, merci de le déplacer dans ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php';
$lng['install']['creating_configfile_failed'] = 'Impossible de créer ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php, merci de le créer manuellement avec le contenu suivant:'; $lng['install']['creating_configfile_failed'] = 'Impossible de créer ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php, merci de le créer manuellement avec le contenu suivant:';
$lng['install']['froxlor_succ_installed'] = 'Froxlor a été installé avec succès.'; $lng['install']['froxlor_succ_installed'] = 'Froxlor a été installé avec succès.';

View File

@@ -86,7 +86,7 @@ $lng['install']['changing_data'] = 'Einstellungen anpassen...';
$lng['install']['creating_entries'] = 'Trage neue Werte ein...'; $lng['install']['creating_entries'] = 'Trage neue Werte ein...';
$lng['install']['adding_admin_user'] = 'Erstelle Admin-Benutzer...'; $lng['install']['adding_admin_user'] = 'Erstelle Admin-Benutzer...';
$lng['install']['creating_configfile'] = 'Erstelle Konfigurationsdatei...'; $lng['install']['creating_configfile'] = 'Erstelle Konfigurationsdatei...';
$lng['install']['creating_configfile_temp'] = 'Datei wurde in /tmp/userdata.inc.php gespeichert, bitte nach ' . dirname(dirname(__DIR__)) . '/lib/ verschieben.'; $lng['install']['creating_configfile_temp'] = 'Datei wurde in %s gespeichert, bitte nach ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php verschieben.';
$lng['install']['creating_configfile_failed'] = 'Konnte ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php nicht erstellen, bitte manuell mit folgendem Inhalt anlegen:'; $lng['install']['creating_configfile_failed'] = 'Konnte ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php nicht erstellen, bitte manuell mit folgendem Inhalt anlegen:';
$lng['install']['froxlor_succ_installed'] = 'Froxlor wurde erfolgreich installiert.'; $lng['install']['froxlor_succ_installed'] = 'Froxlor wurde erfolgreich installiert.';

View File

@@ -546,7 +546,7 @@ if (\Froxlor\Froxlor::isFroxlorVersion('0.10.10')) {
if (\Froxlor\Froxlor::isDatabaseVersion('201912311')) { if (\Froxlor\Froxlor::isDatabaseVersion('201912311')) {
showUpdateStep("Migrate logfiles_format setting"); showUpdateStep("Migrate logfiles_format setting");
$current_format = Settings::Set('system.logfiles_format'); $current_format = Settings::Set('system.logfiles_format');
if (!empty($current_format)) { if (! empty($current_format)) {
Settings::Set('system.logfiles_format', '"' . Settings::Get('system.logfiles_format') . '"'); Settings::Set('system.logfiles_format', '"' . Settings::Get('system.logfiles_format') . '"');
lastStepStatus(0); lastStepStatus(0);
} else { } else {
@@ -571,3 +571,70 @@ if (\Froxlor\Froxlor::isFroxlorVersion('0.10.12')) {
showUpdateStep("Updating from 0.10.12 to 0.10.13", false); showUpdateStep("Updating from 0.10.12 to 0.10.13", false);
\Froxlor\Froxlor::updateToVersion('0.10.13'); \Froxlor\Froxlor::updateToVersion('0.10.13');
} }
if (\Froxlor\Froxlor::isDatabaseVersion('201912313')) {
showUpdateStep("Adding new field to domains table");
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ADD `domain_ace` varchar(255) NOT NULL default '' AFTER `domain`;");
lastStepStatus(0);
showUpdateStep("Updating domain entries");
$upd_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `domain_ace` = :ace WHERE `id` = :domainid");
$sel_stmt = Database::prepare("SELECT id, domain FROM `" . TABLE_PANEL_DOMAINS . "` ORDER BY id ASC");
Database::pexecute($sel_stmt);
$idna_convert = new \Froxlor\Idna\IdnaWrapper();
while ($domain = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
Database::pexecute($upd_stmt, [
'ace' => $idna_convert->decode($domain['domain']),
'domainid' => $domain['id']
]);
}
lastStepStatus(0);
\Froxlor\Froxlor::updateToDbVersion('202002290');
}
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.13')) {
showUpdateStep("Updating from 0.10.13 to 0.10.14", false);
\Froxlor\Froxlor::updateToVersion('0.10.14');
}
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.14')) {
showUpdateStep("Updating from 0.10.14 to 0.10.15", false);
\Froxlor\Froxlor::updateToVersion('0.10.15');
}
if (\Froxlor\Froxlor::isDatabaseVersion('202002290')) {
showUpdateStep("Adding new setting to validate DNS when using Let's Encrypt");
Database::query("DELETE FROM `" . TABLE_PANEL_SETTINGS . "` WHERE `settinggroup` = 'system' AND `varname` = 'disable_le_selfcheck'");
$le_domain_dnscheck = isset($_POST['system_le_domain_dnscheck']) ? (int) $_POST['system_le_domain_dnscheck'] : '1';
Settings::AddNew("system.le_domain_dnscheck", $le_domain_dnscheck);
lastStepStatus(0);
\Froxlor\Froxlor::updateToDbVersion('202004140');
}
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.15')) {
showUpdateStep("Updating from 0.10.15 to 0.10.16", false);
\Froxlor\Froxlor::updateToVersion('0.10.16');
}
if (\Froxlor\Froxlor::isDatabaseVersion('202004140')) {
showUpdateStep("Adding unique key on domainid field in domain ssl table");
// check for duplicate entries prior to set a unique key to avoid errors on update
Database::query("
DELETE a.* FROM domain_ssl_settings AS a
LEFT JOIN domain_ssl_settings AS b ON UNIX_TIMESTAMP(b.`expirationdate`) > UNIX_TIMESTAMP(a.`expirationdate`)
AND (b.`domainid`=a.`domainid` OR (UNIX_TIMESTAMP(b.`expirationdate`) = UNIX_TIMESTAMP(a.`expirationdate`) AND b.`id`>a.`id`))
WHERE b.`id` IS NOT NULL
");
Database::query("ALTER TABLE `domain_ssl_settings` ADD UNIQUE(`domainid`)");
lastStepStatus(0);
\Froxlor\Froxlor::updateToDbVersion('202005150');
}
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.16')) {
showUpdateStep("Updating from 0.10.16 to 0.10.17", false);
\Froxlor\Froxlor::updateToVersion('0.10.17');
}

View File

@@ -34,6 +34,9 @@ function getPreConfig($current_version, $current_db_version)
include_once \Froxlor\FileDir::makeCorrectFile(dirname(__FILE__) . '/preconfig/0.9/preconfig_0.9.inc.php'); include_once \Froxlor\FileDir::makeCorrectFile(dirname(__FILE__) . '/preconfig/0.9/preconfig_0.9.inc.php');
parseAndOutputPreconfig($has_preconfig, $return, $current_version, $current_db_version); parseAndOutputPreconfig($has_preconfig, $return, $current_version, $current_db_version);
include_once \Froxlor\FileDir::makeCorrectFile(dirname(__FILE__) . '/preconfig/0.10/preconfig_0.10.inc.php');
parseAndOutputPreconfig2($has_preconfig, $return, $current_version, $current_db_version);
$return .= '<br /><br />' . \Froxlor\UI\HTML::makecheckbox('update_changesagreed', '<strong>I have read the update notifications above and I am aware of the changes made to my system.</strong>', '1', true, '0', true); $return .= '<br /><br />' . \Froxlor\UI\HTML::makecheckbox('update_changesagreed', '<strong>I have read the update notifications above and I am aware of the changes made to my system.</strong>', '1', true, '0', true);
$return .= '</div>'; $return .= '</div>';
$return .= '<input type="hidden" name="update_preconfig" value="1" />'; $return .= '<input type="hidden" name="update_preconfig" value="1" />';

View File

@@ -0,0 +1,42 @@
<?php
/**
* This file is part of the Froxlor project.
* Copyright (c) 2010 the Froxlor Team (see authors).
*
* For the full copyright and license information, please view the COPYING
* file that was distributed with this source code. You can also view the
* COPYING file online at http://files.froxlor.org/misc/COPYING.txt
*
* @copyright (c) the authors
* @author Froxlor team <team@froxlor.org> (2010-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Updater
*
*/
/**
* checks if the new-version has some updating to do
*
* @param boolean $has_preconfig
* pointer to check if any preconfig has to be output
* @param string $return
* pointer to output string
* @param string $current_version
* current froxlor version
*
* @return null
*/
function parseAndOutputPreconfig2(&$has_preconfig, &$return, $current_version, $current_db_version)
{
global $lng;
if (versionInUpdate($current_db_version, '202004140')) {
$has_preconfig = true;
$description = 'Froxlor can now optionally validate the dns entries of domains that request Lets Encrypt certificates to reduce dns-related problems (e.g. freshly registered domain or updated a-record).<br />';
$question = '<strong>Validate DNS of domains when using Lets Encrypt&nbsp;';
$question .= \Froxlor\UI\HTML::makeyesno('system_le_domain_dnscheck', '1', '0', '1');
eval("\$return.=\"" . \Froxlor\UI\Template::getTemplate("update/preconfigitem") . "\";");
}
}

View File

@@ -54,6 +54,13 @@ abstract class ApiCommand extends ApiParameter
*/ */
private $mail = null; private $mail = null;
/**
* whether the call is an internal one or not
*
* @var boolean
*/
private $internal_call = false;
/** /**
* language strings array * language strings array
* *
@@ -90,10 +97,12 @@ abstract class ApiCommand extends ApiParameter
* optional, array of parameters (var=>value) for the command * optional, array of parameters (var=>value) for the command
* @param array $userinfo * @param array $userinfo
* optional, passed via WebInterface (instead of $header) * optional, passed via WebInterface (instead of $header)
* @param boolean $internal
* optional whether called internally, default false
* *
* @throws \Exception * @throws \Exception
*/ */
public function __construct($header = null, $params = null, $userinfo = null) public function __construct($header = null, $params = null, $userinfo = null, $internal = false)
{ {
parent::__construct($params); parent::__construct($params);
@@ -127,6 +136,9 @@ abstract class ApiCommand extends ApiParameter
if ($this->debug) { if ($this->debug) {
$this->logger()->logAction(\Froxlor\FroxlorLogger::LOG_ERROR, LOG_DEBUG, "[API] " . get_called_class() . ": " . json_encode($params, JSON_UNESCAPED_SLASHES)); $this->logger()->logAction(\Froxlor\FroxlorLogger::LOG_ERROR, LOG_DEBUG, "[API] " . get_called_class() . ": " . json_encode($params, JSON_UNESCAPED_SLASHES));
} }
// set internal call flag
$this->internal_call = $internal;
} }
/** /**
@@ -191,13 +203,15 @@ abstract class ApiCommand extends ApiParameter
* array of user-data * array of user-data
* @param array $params * @param array $params
* array of parameters for the command * array of parameters for the command
* @param boolean $internal
* optional whether called internally, default false
* *
* @return ApiCommand * @return ApiCommand
* @throws \Exception * @throws \Exception
*/ */
public static function getLocal($userinfo = null, $params = null) public static function getLocal($userinfo = null, $params = null, $internal = false)
{ {
return new static(null, $params, $userinfo); return new static(null, $params, $userinfo, $internal);
} }
/** /**
@@ -210,6 +224,16 @@ abstract class ApiCommand extends ApiParameter
return $this->is_admin; return $this->is_admin;
} }
/**
* internal call flag
*
* @return boolean
*/
protected function isInternal()
{
return $this->internal_call;
}
/** /**
* return field from user-table * return field from user-table
* *
@@ -241,7 +265,7 @@ abstract class ApiCommand extends ApiParameter
* optional array of placeholders mapped to the actual value which is used in the API commands when executing the statement [internal] * optional array of placeholders mapped to the actual value which is used in the API commands when executing the statement [internal]
* @param boolean $append * @param boolean $append
* optional append to WHERE clause rather then create new one, default false [internal] * optional append to WHERE clause rather then create new one, default false [internal]
* *
* @return string * @return string
*/ */
protected function getSearchWhere(&$query_fields = array(), $append = false) protected function getSearchWhere(&$query_fields = array(), $append = false)
@@ -304,7 +328,7 @@ abstract class ApiCommand extends ApiParameter
* optional, limit resultset, default 0 * optional, limit resultset, default 0
* @param int $sql_offset * @param int $sql_offset
* optional, offset for limitation, default 0 * optional, offset for limitation, default 0
* *
* @return string * @return string
*/ */
protected function getLimit() protected function getLimit()
@@ -333,7 +357,7 @@ abstract class ApiCommand extends ApiParameter
* optional array with index = fieldname and value = ASC|DESC * optional array with index = fieldname and value = ASC|DESC
* @param boolean $append * @param boolean $append
* optional append to ORDER BY clause rather then create new one, default false [internal] * optional append to ORDER BY clause rather then create new one, default false [internal]
* *
* @return string * @return string
*/ */
protected function getOrderBy($append = false) protected function getOrderBy($append = false)
@@ -417,15 +441,18 @@ abstract class ApiCommand extends ApiParameter
* *
* @param string $command * @param string $command
* @param array|null $params * @param array|null $params
* * @param boolean $internal
* optional whether called internally, default false
*
*
* @return array * @return array
*/ */
protected function apiCall($command = null, $params = null) protected function apiCall($command = null, $params = null, $internal = false)
{ {
$_command = explode(".", $command); $_command = explode(".", $command);
$module = __NAMESPACE__ . "\Commands\\" . $_command[0]; $module = __NAMESPACE__ . "\Commands\\" . $_command[0];
$function = $_command[1]; $function = $_command[1];
$json_result = $module::getLocal($this->getUserData(), $params)->{$function}(); $json_result = $module::getLocal($this->getUserData(), $params, $internal)->{$function}();
return json_decode($json_result, true)['data']; return json_decode($json_result, true)['data'];
} }
@@ -491,7 +518,7 @@ abstract class ApiCommand extends ApiParameter
$customer_ids[] = $customer['customerid']; $customer_ids[] = $customer['customerid'];
} }
} else { } else {
if (! empty($customer_hide_option) && \Froxlor\Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { if (!$this->isInternal() && ! empty($customer_hide_option) && \Froxlor\Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) {
throw new \Exception("You cannot access this resource", 405); throw new \Exception("You cannot access this resource", 405);
} }
$customer_ids = array( $customer_ids = array(

View File

@@ -689,6 +689,7 @@ class Customers extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resource
'name' => $name, 'name' => $name,
'company' => $company 'company' => $company
)), )),
'CUSTOMER_NO' => $customernumber,
'USERNAME' => $loginname, 'USERNAME' => $loginname,
'PASSWORD' => $password, 'PASSWORD' => $password,
'SERVER_HOSTNAME' => $srv_hostname, 'SERVER_HOSTNAME' => $srv_hostname,
@@ -1409,7 +1410,7 @@ class Customers extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resource
'id' => $id 'id' => $id
), true, true); ), true, true);
// first gather all domain-id's to clean up panel_domaintoip and dns-entries accordingly // first gather all domain-id's to clean up panel_domaintoip, dns-entries and certificates accordingly
$did_stmt = Database::prepare("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); $did_stmt = Database::prepare("SELECT `id` FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id");
Database::pexecute($did_stmt, array( Database::pexecute($did_stmt, array(
'id' => $id 'id' => $id
@@ -1425,6 +1426,11 @@ class Customers extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resource
Database::pexecute($stmt, array( Database::pexecute($stmt, array(
'did' => $row['id'] 'did' => $row['id']
), true, true); ), true, true);
// remove domain->certificates entries
$stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = :did");
Database::pexecute($stmt, array(
'did' => $row['id']
), true, true);
} }
// remove customer domains // remove customer domains
$stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id"); $stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAINS . "` WHERE `customerid` = :id");

View File

@@ -684,6 +684,7 @@ class Domains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEn
$ins_data = array( $ins_data = array(
'domain' => $domain, 'domain' => $domain,
'domain_ace' => $idna_convert->decode($domain),
'customerid' => $customerid, 'customerid' => $customerid,
'adminid' => $adminid, 'adminid' => $adminid,
'documentroot' => $documentroot, 'documentroot' => $documentroot,
@@ -732,6 +733,7 @@ class Domains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEn
$ins_stmt = Database::prepare(" $ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET
`domain` = :domain, `domain` = :domain,
`domain_ace` = :domain_ace,
`customerid` = :customerid, `customerid` = :customerid,
`adminid` = :adminid, `adminid` = :adminid,
`documentroot` = :documentroot, `documentroot` = :documentroot,

View File

@@ -100,8 +100,8 @@ class EmailAccounts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Reso
// alternative email address to send info to // alternative email address to send info to
if (Settings::Get('panel.sendalternativemail') == 1) { if (Settings::Get('panel.sendalternativemail') == 1) {
$alternative_email = $idna_convert->encode(\Froxlor\Validate\Validate::validate($alternative_email, 'alternative_email', '', '', array(), true)); $alternative_email = $idna_convert->encode(\Froxlor\Validate\Validate::validate($alternative_email, 'alternative_email', '', '', array(), true));
if (! \Froxlor\Validate\Validate::validateEmail($alternative_email)) { if (!empty($alternative_email) && ! \Froxlor\Validate\Validate::validateEmail($alternative_email)) {
\Froxlor\UI\Response::standard_error('emailiswrong', $alternative_email, true); \Froxlor\UI\Response::standard_error('alternativeemailiswrong', $alternative_email, true);
} }
} else { } else {
$alternative_email = ''; $alternative_email = '';
@@ -192,7 +192,12 @@ class EmailAccounts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Reso
$replace_arr = array( $replace_arr = array(
'EMAIL' => $email_full, 'EMAIL' => $email_full,
'USERNAME' => $username, 'USERNAME' => $username,
'PASSWORD' => $password 'PASSWORD' => $password,
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($customer),
'NAME' => $customer['name'],
'FIRSTNAME' => $customer['firstname'],
'COMPANY' => $customer['company'],
'CUSTOMER_NO' => $customer['customernumber']
); );
// get the customers admin // get the customers admin
@@ -231,7 +236,7 @@ class EmailAccounts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Reso
$this->mailer()->clearAddresses(); $this->mailer()->clearAddresses();
// customer wants to send the e-mail to an alternative email address too // customer wants to send the e-mail to an alternative email address too
if (Settings::Get('panel.sendalternativemail') == 1) { if (Settings::Get('panel.sendalternativemail') == 1 && !empty($alternative_email)) {
// get template for mail subject // get template for mail subject
$mail_subject = $this->getMailTemplate($customer, 'mails', 'pop_success_alternative_subject', $replace_arr, $this->lng['mails']['pop_success_alternative']['subject']); $mail_subject = $this->getMailTemplate($customer, 'mails', 'pop_success_alternative_subject', $replace_arr, $this->lng['mails']['pop_success_alternative']['subject']);
// get template for mail body // get template for mail body

View File

@@ -62,9 +62,10 @@ class Emails extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEnt
} }
// check domain and whether it's an email-enabled domain // check domain and whether it's an email-enabled domain
// use internal call because the customer might have 'domains' in customer_hide_options
$domain_check = $this->apiCall('SubDomains.get', array( $domain_check = $this->apiCall('SubDomains.get', array(
'domainname' => $domain 'domainname' => $domain
)); ), true);
if ($domain_check['isemaildomain'] == 0) { if ($domain_check['isemaildomain'] == 0) {
\Froxlor\UI\Response::standard_error('maindomainnonexist', $domain, true); \Froxlor\UI\Response::standard_error('maindomainnonexist', $domain, true);
} }

View File

@@ -243,6 +243,26 @@ class Froxlor extends \Froxlor\Api\ApiCommand
return $this->response(200, "successfull", \Froxlor\System\Crypt::generatePassword()); return $this->response(200, "successfull", \Froxlor\System\Crypt::generatePassword());
} }
/**
* can be used to remotely run the integritiy checks froxlor implements
*
* @access admin
* @throws \Exception
* @return string
*/
public function integrityCheck()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$integrity = new \Froxlor\Database\IntegrityCheck();
$result = $integrity->checkAll();
if ($result) {
return $this->response(200, "successfull", "OK");
}
throw new \Exception("Some checks failed.", 406);
}
throw new \Exception("Not allowed to execute given command.", 403);
}
/** /**
* returns a list of all available api functions * returns a list of all available api functions
* *

View File

@@ -227,6 +227,10 @@ class Ftps extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEntit
$replace_arr = array( $replace_arr = array(
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($customer), 'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($customer),
'CUST_NAME' => \Froxlor\User::getCorrectUserSalutation($customer), // < keep this for compatibility 'CUST_NAME' => \Froxlor\User::getCorrectUserSalutation($customer), // < keep this for compatibility
'NAME' => $customer['name'],
'FIRSTNAME' => $customer['firstname'],
'COMPANY' => $customer['company'],
'CUSTOMER_NO' => $customer['customernumber'],
'USR_NAME' => $username, 'USR_NAME' => $username,
'USR_PASS' => $password, 'USR_PASS' => $password,
'USR_PATH' => \Froxlor\FileDir::makeCorrectDir(str_replace($customer['documentroot'], "/", $path)) 'USR_PATH' => \Froxlor\FileDir::makeCorrectDir(str_replace($customer['documentroot'], "/", $path))

View File

@@ -170,7 +170,7 @@ class IpsAndPorts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
{ {
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) { if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, false, true); $ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip'), false, 'invalidip', false, true, false, false, true);
$port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, 80), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array( $port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, 80), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array(
'stringisempty', 'stringisempty',
'myport' 'myport'
@@ -367,7 +367,7 @@ class IpsAndPorts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
'id' => $id 'id' => $id
)); ));
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, false, false, false, true); $ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, true, false, false, true);
$port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, $result['port']), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array( $port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, $result['port']), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array(
'stringisempty', 'stringisempty',
'myport' 'myport'
@@ -560,7 +560,7 @@ class IpsAndPorts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
'ip' => $result['ip'] 'ip' => $result['ip']
)); ));
if (($result['ip'] != Settings::Get('system.ipaddress')) || ($result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport == false)) { if (($result['ip'] != Settings::Get('system.ipaddress')) || ($result['ip'] == Settings::Get('system.ipaddress') && $result_sameipotherport != false)) {
$del_stmt = Database::prepare(" $del_stmt = Database::prepare("
DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "` DELETE FROM `" . TABLE_PANEL_IPSANDPORTS . "`

View File

@@ -125,6 +125,10 @@ class Mysqls extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEnt
$replace_arr = array( $replace_arr = array(
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($userinfo), 'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($userinfo),
'CUST_NAME' => \Froxlor\User::getCorrectUserSalutation($userinfo), // < keep this for compatibility 'CUST_NAME' => \Froxlor\User::getCorrectUserSalutation($userinfo), // < keep this for compatibility
'NAME' => $userinfo['name'],
'FIRSTNAME' => $userinfo['firstname'],
'COMPANY' => $userinfo['company'],
'CUSTOMER_NO' => $userinfo['customernumber'],
'DB_NAME' => $username, 'DB_NAME' => $username,
'DB_PASS' => $password, 'DB_PASS' => $password,
'DB_DESC' => $databasedescription, 'DB_DESC' => $databasedescription,

View File

@@ -256,6 +256,7 @@ class SubDomains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resourc
`customerid` = :customerid, `customerid` = :customerid,
`adminid` = :adminid, `adminid` = :adminid,
`domain` = :domain, `domain` = :domain,
`domain_ace` = :domain_ace,
`documentroot` = :documentroot, `documentroot` = :documentroot,
`aliasdomain` = :aliasdomain, `aliasdomain` = :aliasdomain,
`parentdomainid` = :parentdomainid, `parentdomainid` = :parentdomainid,
@@ -287,6 +288,7 @@ class SubDomains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resourc
"customerid" => $customer['customerid'], "customerid" => $customer['customerid'],
"adminid" => $customer['adminid'], "adminid" => $customer['adminid'],
"domain" => $completedomain, "domain" => $completedomain,
"domain_ace" => $idna_convert->decode($completedomain),
"documentroot" => $path, "documentroot" => $path,
"aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null, "aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null,
"parentdomainid" => $domain_check['id'], "parentdomainid" => $domain_check['id'],
@@ -407,7 +409,7 @@ class SubDomains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resourc
); );
} }
} else { } else {
if (Settings::IsInList('panel.customer_hide_options', 'domains')) { if (! $this->isInternal() && Settings::IsInList('panel.customer_hide_options', 'domains')) {
throw new \Exception("You cannot access this resource", 405); throw new \Exception("You cannot access this resource", 405);
} }
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
@@ -765,6 +767,7 @@ class SubDomains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resourc
'`d`.`id`', '`d`.`id`',
'`d`.`customerid`', '`d`.`customerid`',
'`d`.`domain`', '`d`.`domain`',
'`d`.`domain_ace`',
'`d`.`documentroot`', '`d`.`documentroot`',
'`d`.`isbinddomain`', '`d`.`isbinddomain`',
'`d`.`isemaildomain`', '`d`.`isemaildomain`',
@@ -780,7 +783,7 @@ class SubDomains extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resourc
// prepare select statement // prepare select statement
$domains_stmt = Database::prepare(" $domains_stmt = Database::prepare("
SELECT " . implode(",", $select_fields) . ", IF(`d`.`parentdomainid` > 0, `pd`.`domain`, `d`.`domain`) AS `parentdomainname`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`, `da`.`id` AS `domainaliasid`, `da`.`domain` AS `domainalias` SELECT " . implode(",", $select_fields) . ", IF(`d`.`parentdomainid` > 0, `pd`.`domain_ace`, `d`.`domain_ace`) AS `parentdomainname`, `ad`.`id` AS `aliasdomainid`, `ad`.`domain` AS `aliasdomain`, `da`.`id` AS `domainaliasid`, `da`.`domain` AS `domainalias`
FROM `" . TABLE_PANEL_DOMAINS . "` `d` FROM `" . TABLE_PANEL_DOMAINS . "` `d`
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `ad` ON `d`.`aliasdomain`=`ad`.`id`
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id` LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` `da` ON `da`.`aliasdomain`=`d`.`id`

View File

@@ -56,7 +56,7 @@ class FroxlorRPC
private static function validateAuth($key, $secret) private static function validateAuth($key, $secret)
{ {
$sel_stmt = \Froxlor\Database\Database::prepare(" $sel_stmt = \Froxlor\Database\Database::prepare("
SELECT ak.*, a.api_allowed as admin_api_allowed, c.api_allowed as cust_api_allowed SELECT ak.*, a.api_allowed as admin_api_allowed, c.api_allowed as cust_api_allowed, c.deactivated
FROM `api_keys` ak FROM `api_keys` ak
LEFT JOIN `panel_admins` a ON a.adminid = ak.adminid LEFT JOIN `panel_admins` a ON a.adminid = ak.adminid
LEFT JOIN `panel_customers` c ON c.customerid = ak.customerid LEFT JOIN `panel_customers` c ON c.customerid = ak.customerid
@@ -67,7 +67,7 @@ class FroxlorRPC
'as' => $secret 'as' => $secret
), true, true); ), true, true);
if ($result) { if ($result) {
if ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == - 1 || $result['valid_until'] >= time()) && (($result['customerid'] == 0 && $result['admin_api_allowed'] == 1) || ($result['customerid'] > 0 && $result['cust_api_allowed'] == 1))) { if ($result['apikey'] == $key && $result['secret'] == $secret && ($result['valid_until'] == - 1 || $result['valid_until'] >= time()) && (($result['customerid'] == 0 && $result['admin_api_allowed'] == 1) || ($result['customerid'] > 0 && $result['cust_api_allowed'] == 1 && $result['deactivated'] == 0))) {
// get user to check whether api call is allowed // get user to check whether api call is allowed
if (! empty($result['allowed_from'])) { if (! empty($result['allowed_from'])) {
// @todo allow specification and validating of whole subnets later // @todo allow specification and validating of whole subnets later

View File

@@ -99,7 +99,7 @@ class CronConfig
$binpath = Settings::Get("system.croncmdline"); $binpath = Settings::Get("system.croncmdline");
// fallback as it is important // fallback as it is important
if ($binpath === null) { if ($binpath === null) {
$binpath = "/usr/bin/nice -n 5 /usr/bin/php5 -q"; $binpath = "/usr/bin/nice -n 5 /usr/bin/php -q";
} }
$cronfile .= "root " . $binpath . " " . \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . "/scripts/froxlor_master_cronjob.php") . " --" . $row_cronentry['cronfile'] . " 1> /dev/null\n"; $cronfile .= "root " . $binpath . " " . \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . "/scripts/froxlor_master_cronjob.php") . " --" . $row_cronentry['cronfile'] . " 1> /dev/null\n";

View File

@@ -172,7 +172,7 @@ class Apache extends HttpConfigBase
$mypath = $this->getMyPath($row_ipsandports); $mypath = $this->getMyPath($row_ipsandports);
$this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . $mypath . '"' . "\n"; $this->virtualhosts_data[$vhosts_filename] .= 'DocumentRoot "' . rtrim($mypath, "/") . '"' . "\n";
if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') { if ($row_ipsandports['vhostcontainer_servername_statement'] == '1') {
$this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n"; $this->virtualhosts_data[$vhosts_filename] .= ' ServerName ' . Settings::Get('system.hostname') . "\n";
@@ -661,7 +661,7 @@ class Apache extends HttpConfigBase
if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') { if ($domain['deactivated'] == '1' && Settings::Get('system.deactivateddocroot') != '') {
$webroot_text .= ' # Using docroot for deactivated users...' . "\n"; $webroot_text .= ' # Using docroot for deactivated users...' . "\n";
$webroot_text .= ' DocumentRoot "' . \Froxlor\FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . "\"\n"; $webroot_text .= ' DocumentRoot "' . rtrim(\Froxlor\FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')), "/") . "\"\n";
$webroot_text .= ' <Directory "' . \Froxlor\FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '">' . "\n"; $webroot_text .= ' <Directory "' . \Froxlor\FileDir::makeCorrectDir(Settings::Get('system.deactivateddocroot')) . '">' . "\n";
// >=apache-2.4 enabled? // >=apache-2.4 enabled?
if (Settings::Get('system.apache24') == '1') { if (Settings::Get('system.apache24') == '1') {
@@ -674,7 +674,7 @@ class Apache extends HttpConfigBase
$webroot_text .= ' </Directory>' . "\n"; $webroot_text .= ' </Directory>' . "\n";
$this->deactivated = true; $this->deactivated = true;
} else { } else {
$webroot_text .= ' DocumentRoot "' . $domain['documentroot'] . "\"\n"; $webroot_text .= ' DocumentRoot "' . rtrim($domain['documentroot'], "/") . "\"\n";
$this->deactivated = false; $this->deactivated = false;
} }

View File

@@ -4,6 +4,8 @@ namespace Froxlor\Cron\Http\LetsEncrypt;
use Froxlor\FroxlorLogger; use Froxlor\FroxlorLogger;
use Froxlor\Settings; use Froxlor\Settings;
use Froxlor\Database\Database; use Froxlor\Database\Database;
use Froxlor\PhpHelper;
use Froxlor\Domain\Domain;
/** /**
* This file is part of the Froxlor project. * This file is part of the Froxlor project.
@@ -18,9 +20,9 @@ use Froxlor\Database\Database;
* @author Froxlor team <team@froxlor.org> (2016-) * @author Froxlor team <team@froxlor.org> (2016-)
* @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt
* @package Cron * @package Cron
* *
* @since 0.9.35 * @since 0.9.35
* *
*/ */
class AcmeSh extends \Froxlor\Cron\FroxlorCron class AcmeSh extends \Froxlor\Cron\FroxlorCron
{ {
@@ -45,7 +47,406 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
public static $no_inserttask = false; public static $no_inserttask = false;
private static function needRenew() /**
* run the task
*
* @param boolean $internal
* @return number
*/
public static function run($internal = false)
{
// usually, this is action is called from within the tasks-jobs
if (! defined('CRON_IS_FORCED') && ! defined('CRON_DEBUG_FLAG') && $internal == false) {
// Let's Encrypt cronjob is combined with regeneration of webserver configuration files.
// For debugging purposes you can use the --debug switch and the --force switch to run the cron manually.
// check whether we MIGHT need to run although there is no task to regenerate config-files
$needRenew = self::issueDomains();
if ($needRenew || self::issueFroxlorVhost()) {
// insert task to generate certificates and vhost-configs
\Froxlor\System\Cronjob::inserttask(1);
}
return 0;
}
// set server according to settings
self::$apiserver = 'https://acme-' . (Settings::Get('system.letsencryptca') == 'testing' ? 'staging-' : '') . 'v0' . \Froxlor\Settings::Get('system.leapiversion') . '.api.letsencrypt.org/directory';
// validate acme.sh installation
if (! self::checkInstall()) {
return - 1;
}
// flag for re-generation of vhost files
$changedetected = 0;
// prepare update sql
self::$updcert_stmt = Database::prepare("
REPLACE INTO
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET
`id` = :id,
`domainid` = :domainid,
`ssl_cert_file` = :crt,
`ssl_key_file` = :key,
`ssl_ca_file` = :ca,
`ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate
");
// prepare domain update sql
self::$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid");
// check whether there are certificates to issue
$issue_froxlor = self::issueFroxlorVhost();
$issue_domains = self::issueDomains();
// first - generate LE for system-vhost if enabled
if ($issue_froxlor) {
// build row
$certrow = array(
'loginname' => 'froxlor.panel',
'domain' => Settings::Get('system.hostname'),
'domainid' => 0,
'documentroot' => \Froxlor\Froxlor::getInstallDir(),
'leprivatekey' => Settings::Get('system.leprivatekey'),
'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null,
'ssl_cert_file' => null,
'ssl_key_file' => null,
'ssl_ca_file' => null,
'ssl_csr_file' => null,
'id' => null
);
// add to queue
$issue_domains[] = $certrow;
}
if (count($issue_domains)) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Requesting " . count($issue_domains) . " new Let's Encrypt certificates");
self::runIssueFor($issue_domains);
$changedetected = 1;
}
// compare file-system certificates with the ones in our database
// and update if needed
$renew_froxlor = self::renewFroxlorVhost();
$renew_domains = self::renewDomains();
if ($renew_froxlor) {
// build row
$certrow = array(
'loginname' => 'froxlor.panel',
'domain' => Settings::Get('system.hostname'),
'domainid' => 0,
'documentroot' => \Froxlor\Froxlor::getInstallDir(),
'leprivatekey' => Settings::Get('system.leprivatekey'),
'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => is_array($renew_froxlor) ? $renew_froxlor['expirationdate'] : date('Y-m-d H:i:s', 0),
'ssl_cert_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_cert_file'] : null,
'ssl_key_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_key_file'] : null,
'ssl_ca_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_ca_file'] : null,
'ssl_csr_file' => is_array($renew_froxlor) ? $renew_froxlor['ssl_csr_file'] : null,
'id' => is_array($renew_froxlor) ? $renew_froxlor['id'] : null
);
$renew_domains[] = $certrow;
}
foreach ($renew_domains as $domain) {
$cronlog = FroxlorLogger::getInstanceOf(array(
'loginname' => $domain['loginname'],
'adminsession' => 0
));
if (defined('CRON_IS_FORCED') || self::checkFsFilesAreNewer($domain['domain'], $domain['expirationdate'])) {
self::certToDb($domain, $cronlog, array());
$changedetected = 1;
}
}
// If we have a change in a certificate, we need to update the webserver - configs
// This is easiest done by just creating a new task ;)
if ($changedetected) {
if (self::$no_inserttask == false) {
\Froxlor\System\Cronjob::inserttask(1);
}
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Let's Encrypt certificates have been updated");
} else {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "No new certificates or certificate updates found");
}
}
/**
* issue certificates for a list of domains
*/
private static function runIssueFor($certrows = array())
{
// prepare aliasdomain-check
$aliasdomains_stmt = Database::prepare("
SELECT
dom.`id` as domainid,
dom.`domain`,
dom.`wwwserveralias`
FROM `" . TABLE_PANEL_DOMAINS . "` AS dom
WHERE
dom.`aliasdomain` = :id
AND dom.`letsencrypt` = 1
AND dom.`iswildcarddomain` = 0
");
// iterate through all domains
foreach ($certrows as $certrow) {
// set logger to corresponding loginname for the log to appear in the users system-log
$cronlog = FroxlorLogger::getInstanceOf(array(
'loginname' => $certrow['loginname'],
'adminsession' => 0
));
// Only issue let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) {
$do_force = false;
if (! empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) {
// domain changed (SAN or similar)
$do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Creating certificate for " . $certrow['domain']);
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding common-name: " . $certrow['domain']);
$domains = array(
strtolower($certrow['domain'])
);
// add www.<domain> to SAN list
if ($certrow['wwwserveralias'] == 1) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $certrow['domain']);
$domains[] = strtolower('www.' . $certrow['domain']);
}
if ($certrow['domainid'] == 0) {
$froxlor_aliases = Settings::Get('system.froxloraliases');
if (! empty($froxlor_aliases)) {
$froxlor_aliases = explode(",", $froxlor_aliases);
foreach ($froxlor_aliases as $falias) {
if (\Froxlor\Validate\Validate::validateDomain(trim($falias))) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: " . strtolower(trim($falias)));
$domains[] = strtolower(trim($falias));
}
}
}
} else {
// add alias domains (and possibly www.<aliasdomain>) to SAN list
Database::pexecute($aliasdomains_stmt, array(
'id' => $certrow['domainid']
));
$aliasdomains = $aliasdomains_stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach ($aliasdomains as $aliasdomain) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $aliasdomain['domain']);
$domains[] = strtolower($aliasdomain['domain']);
if ($aliasdomain['wwwserveralias'] == 1) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $aliasdomain['domain']);
$domains[] = strtolower('www.' . $aliasdomain['domain']);
}
}
}
self::validateDns($domains, $certrow['domainid'], $cronlog);
self::runAcmeSh($certrow, $domains, $cronlog, $do_force);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect");
}
}
}
/**
* validate dns (A / AAAA record) of domain against known system ips
*
* @param array $domains
* @param int $domain_id
* @param FroxlorLogger $cronlog
*/
private static function validateDns(&$domains = array(), $domain_id, &$cronlog)
{
if (Settings::Get('system.le_domain_dnscheck') == '1' && ! empty($domains)) {
$loop_domains = $domains;
// ips according to our system
$our_ips = Domain::getIpsOfDomain($domain_id);
foreach ($loop_domains as $idx => $domain) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Validating DNS of " . $domain);
// ips accordint to NS
$domain_ips = PhpHelper::gethostbynamel6($domain);
if (count(array_intersect($our_ips, $domain_ips)) <= 0) {
// no common ips...
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $domain . " due to no system known IP address via DNS check");
unset($domains[$idx]);
}
}
}
}
private static function runAcmeSh($certrow = array(), $domains = array(), &$cronlog = null, $force = false)
{
if (! empty($domains)) {
if (self::$do_update) {
self::checkUpgrade();
self::$do_update = false;
}
$acmesh_cmd = self::$acmesh . " --auto-upgrade 0 --server " . self::$apiserver . " --issue -d " . implode(" -d ", $domains);
// challenge path
$acmesh_cmd .= " -w " . Settings::Get('system.letsencryptchallengepath');
if (Settings::Get('system.leecc') > 0) {
// ecc certificate
$acmesh_cmd .= " --keylength ec-" . Settings::Get('system.leecc');
} else {
$acmesh_cmd .= " --keylength " . Settings::Get('system.letsencryptkeysize');
}
if (Settings::Get('system.letsencryptreuseold') != '1') {
$acmesh_cmd .= " --always-force-new-domain-key";
}
if (Settings::Get('system.letsencryptca') == 'testing') {
$acmesh_cmd .= " --staging";
}
if ($force) {
$acmesh_cmd .= " --force";
}
if (defined('CRON_DEBUG_FLAG')) {
$acmesh_cmd .= " --debug";
}
$acme_result = \Froxlor\FileDir::safe_exec($acmesh_cmd);
// debug output of acme.sh run
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, implode("\n", $acme_result));
self::certToDb($certrow, $cronlog, $acme_result);
}
}
private static function certToDb($certrow, &$cronlog, $acme_result)
{
$return = array();
self::readCertificateToVar(strtolower($certrow['domain']), $return, $cronlog);
if (! empty($return['crt'])) {
$newcert = openssl_x509_parse($return['crt']);
if ($newcert) {
// Store the new data
Database::pexecute(self::$updcert_stmt, array(
'id' => $certrow['id'],
'domainid' => $certrow['domainid'],
'crt' => $return['crt'],
'key' => $return['key'],
'ca' => $return['chain'],
'chain' => $return['chain'],
'csr' => $return['csr'],
'fullchain' => $return['fullchain'],
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t'])
));
if ($certrow['ssl_redirect'] == 3) {
Database::pexecute(self::$upddom_stmt, array(
'domainid' => $certrow['domainid']
));
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Got non-successful Let's Encrypt response for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
}
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
}
}
/**
* check whether we need to issue a new certificate for froxlor itself
*
* @return boolean
*/
private static function issueFroxlorVhost()
{
if (Settings::Get('system.le_froxlor_enabled') == '1') {
// let's encrypt is enabled, now check whether we have a certificate
$froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0'
");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate
if (! $froxlor_ssl && ! self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s'))) {
return true;
}
}
return false;
}
/**
* check whether we need to renew-check the certificate for froxlor itself
*
* @return boolean
*/
private static function renewFroxlorVhost()
{
if (Settings::Get('system.le_froxlor_enabled') == '1') {
// let's encrypt is enabled, now check whether we have a certificate
$froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0'
");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
// also check for possible existing certificate
if ($froxlor_ssl || (! $froxlor_ssl && ! self::checkFsFilesAreNewer(Settings::Get('system.hostname'), date('Y-m-d H:i:s', 0)))) {
return ($froxlor_ssl ? $froxlor_ssl : true);
}
}
return false;
}
/**
* get a list of domains that have a lets encrypt certificate (possible renew)
*/
private static function renewDomains()
{
$certificates_stmt = Database::query("
SELECT
domssl.`id`,
domssl.`domainid`,
domssl.`expirationdate`,
domssl.`ssl_cert_file`,
domssl.`ssl_key_file`,
dom.`domain`,
dom.`id` AS 'domainid',
dom.`ssl_redirect`,
cust.`loginname`
FROM
`" . TABLE_PANEL_CUSTOMERS . "` AS cust,
`" . TABLE_PANEL_DOMAINS . "` AS dom
LEFT JOIN
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` AS domssl ON
dom.`id` = domssl.`domainid`
WHERE
dom.`customerid` = cust.`customerid`
AND cust.deactivated = 0
AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0
");
$renew_certs = $certificates_stmt->fetchAll(\PDO::FETCH_ASSOC);
if ($renew_certs) {
return $renew_certs;
}
return array();
}
/**
* get a list of domains that require a new certificate (issue)
*/
private static function issueDomains()
{ {
$certificates_stmt = Database::query(" $certificates_stmt = Database::query("
SELECT SELECT
@@ -78,322 +479,51 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
AND dom.`letsencrypt` = 1 AND dom.`letsencrypt` = 1
AND dom.`aliasdomain` IS NULL AND dom.`aliasdomain` IS NULL
AND dom.`iswildcarddomain` = 0 AND dom.`iswildcarddomain` = 0
AND ( AND domssl.`expirationdate` IS NULL
domssl.`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY)
OR domssl.`expirationdate` IS NULL
)
"); ");
$customer_ssl = $certificates_stmt->fetchAll(\PDO::FETCH_ASSOC); $customer_ssl = $certificates_stmt->fetchAll(\PDO::FETCH_ASSOC);
if (! $customer_ssl) { if ($customer_ssl) {
$customer_ssl = array(); return $customer_ssl;
} }
return array();
$froxlor_ssl = array();
if (Settings::Get('system.le_froxlor_enabled') == '1') {
$froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
WHERE `domainid` = '0' AND
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL)
");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
if (! $froxlor_ssl) {
$froxlor_ssl = array();
}
}
if (count($customer_ssl) > 0 || count($froxlor_ssl) > 0) {
return array(
'customer_ssl' => $customer_ssl,
'froxlor_ssl' => $froxlor_ssl
);
}
return false;
} }
public static function run($internal = false) private static function checkFsFilesAreNewer($domain, $cert_date = 0)
{
if (! defined('CRON_IS_FORCED') && ! defined('CRON_DEBUG_FLAG') && $internal == false) {
// Let's Encrypt cronjob is combined with regeneration of webserver configuration files.
// For debugging purposes you can use the --debug switch and the --force switch to run the cron manually.
// check whether we MIGHT need to run although there is no task to regenerate config-files
$needRenew = self::needRenew();
if ($needRenew) {
// insert task to generate certificates and vhost-configs
\Froxlor\System\Cronjob::inserttask(1);
}
return 0;
}
self::checkInstall();
self::$apiserver = 'https://acme-'.(Settings::Get('system.letsencryptca') == 'testing' ? 'staging-' : '').'v0' . \Froxlor\Settings::Get('system.leapiversion') . '.api.letsencrypt.org/directory';
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Requesting/renewing Let's Encrypt certificates");
$aliasdomains_stmt = Database::prepare("
SELECT
dom.`id` as domainid,
dom.`domain`,
dom.`wwwserveralias`
FROM `" . TABLE_PANEL_DOMAINS . "` AS dom
WHERE
dom.`aliasdomain` = :id
AND dom.`letsencrypt` = 1
AND dom.`iswildcarddomain` = 0
");
self::$updcert_stmt = Database::prepare("
REPLACE INTO
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
SET
`id` = :id,
`domainid` = :domainid,
`ssl_cert_file` = :crt,
`ssl_key_file` = :key,
`ssl_ca_file` = :ca,
`ssl_cert_chainfile` = :chain,
`ssl_csr_file` = :csr,
`ssl_fullchain_file` = :fullchain,
`expirationdate` = :expirationdate
");
self::$upddom_stmt = Database::prepare("UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `ssl_redirect` = '1' WHERE `id` = :domainid");
// flag for re-generation of vhost files
$changedetected = 0;
$needRenew = self::needRenew();
// first - generate LE for system-vhost if enabled
if (Settings::Get('system.le_froxlor_enabled') == '1') {
$certrow = array(
'loginname' => 'froxlor.panel',
'domain' => Settings::Get('system.hostname'),
'domainid' => 0,
'documentroot' => \Froxlor\Froxlor::getInstallDir(),
'leprivatekey' => Settings::Get('system.leprivatekey'),
'lepublickey' => Settings::Get('system.lepublickey'),
'leregistered' => Settings::Get('system.leregistered'),
'ssl_redirect' => Settings::Get('system.le_froxlor_redirect'),
'expirationdate' => null,
'ssl_cert_file' => null,
'ssl_key_file' => null,
'ssl_ca_file' => null,
'ssl_csr_file' => null,
'id' => null
);
$froxlor_ssl = $needRenew ? $needRenew['froxlor_ssl'] : array();
$cert_mode = 'issue';
if (count($froxlor_ssl) > 0) {
$cert_mode = 'renew';
$certrow['id'] = $froxlor_ssl['id'];
$certrow['expirationdate'] = $froxlor_ssl['expirationdate'];
$certrow['ssl_cert_file'] = $froxlor_ssl['ssl_cert_file'];
$certrow['ssl_key_file'] = $froxlor_ssl['ssl_key_file'];
$certrow['ssl_ca_file'] = $froxlor_ssl['ssl_ca_file'];
$certrow['ssl_csr_file'] = $froxlor_ssl['ssl_csr_file'];
} else {
// check whether we have an entry with valid certificates which just does not need
// updating yet, so we need to skip this here
$froxlor_ssl_settings_stmt = Database::prepare("
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid` = '0'
");
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
if ($froxlor_ssl && ! empty($froxlor_ssl['ssl_cert_file'])) {
$cert_mode = false;
}
}
if ($cert_mode) {
$domains = array(
strtolower($certrow['domain'])
);
$froxlor_aliases = Settings::Get('system.froxloraliases');
if (! empty($froxlor_aliases)) {
$froxlor_aliases = explode(",", $froxlor_aliases);
foreach ($froxlor_aliases as $falias) {
if (\Froxlor\Validate\Validate::validateDomain(trim($falias))) {
$domains[] = strtolower(trim($falias));
}
}
}
// Only renew let's encrypt certificate if no broken ssl_redirect is enabled
// - this temp. deactivation of the ssl-redirect is handled by the webserver-cronjob
$do_force = false;
if ($cert_mode == 'renew') {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updating certificate for " . $certrow['domain']);
} else {
$do_force = true;
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Creating certificate for " . $certrow['domain']);
}
$cronlog = FroxlorLogger::getInstanceOf(array(
'loginname' => $certrow['loginname'],
'adminsession' => 0
));
self::runAcmeSh($certrow, $domains, $cert_mode, $cronlog, $changedetected, $do_force);
}
}
// customer domains
$certrows = $needRenew ? $needRenew['customer_ssl'] : array();
foreach ($certrows as $certrow) {
// initialize mode to 'issue'
$cert_mode = 'issue';
// set logger to corresponding loginname for the log to appear in the users system-log
$cronlog = FroxlorLogger::getInstanceOf(array(
'loginname' => $certrow['loginname'],
'adminsession' => 0
));
// Only renew let's encrypt certificate if no broken ssl_redirect is enabled
if ($certrow['ssl_redirect'] != 2) {
$do_force = false;
if (! empty($certrow['ssl_cert_file']) && ! empty($certrow['expirationdate'])) {
$cert_mode = 'renew';
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updating certificate for " . $certrow['domain']);
} else if (! empty($certrow['ssl_cert_file']) && empty($certrow['expirationdate'])) {
// domain changed (SAN or similar)
$do_force = true;
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Re-creating certificate for " . $certrow['domain']);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Creating certificate for " . $certrow['domain']);
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $certrow['domain']);
$domains = array(
strtolower($certrow['domain'])
);
// add www.<domain> to SAN list
if ($certrow['wwwserveralias'] == 1) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $certrow['domain']);
$domains[] = strtolower('www.' . $certrow['domain']);
}
// add alias domains (and possibly www.<aliasdomain>) to SAN list
Database::pexecute($aliasdomains_stmt, array(
'id' => $certrow['domainid']
));
$aliasdomains = $aliasdomains_stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach ($aliasdomains as $aliasdomain) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: " . $aliasdomain['domain']);
$domains[] = strtolower($aliasdomain['domain']);
if ($aliasdomain['wwwserveralias'] == 1) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Adding SAN entry: www." . $aliasdomain['domain']);
$domains[] = strtolower('www.' . $aliasdomain['domain']);
}
}
self::runAcmeSh($certrow, $domains, $cert_mode, $cronlog, $changedetected, $do_force);
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Skipping Let's Encrypt generation for " . $certrow['domain'] . " due to an enabled ssl_redirect");
}
}
// If we have a change in a certificate, we need to update the webserver - configs
// This is easiest done by just creating a new task ;)
if ($changedetected) {
if (self::$no_inserttask == false) {
\Froxlor\System\Cronjob::inserttask(1);
}
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Let's Encrypt certificates have been updated");
} else {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "No new certificates or certificates due for renewal found");
}
}
private static function runAcmeSh($certrow = array(), $domains = array(), $cert_mode = 'issue', &$cronlog = null, &$changedetected = 0, $force = false)
{
if (! empty($domains)) {
if (self::$do_update) {
self::checkUpgrade();
self::$do_update = false;
}
$acmesh_cmd = self::$acmesh . " --auto-upgrade 0 --server " . self::$apiserver . " --" . $cert_mode . " -d " . implode(" -d ", $domains);
if ($cert_mode == 'issue') {
$acmesh_cmd .= " -w " . Settings::Get('system.letsencryptchallengepath');
}
if (Settings::Get('system.leecc') > 0) {
$acmesh_cmd .= " --keylength ec-" . Settings::Get('system.leecc');
} else {
$acmesh_cmd .= " --keylength " . Settings::Get('system.letsencryptkeysize');
}
if (Settings::Get('system.letsencryptreuseold') != '1') {
$acmesh_cmd .= " --always-force-new-domain-key";
}
if (Settings::Get('system.letsencryptca') == 'testing') {
$acmesh_cmd .= " --staging";
}
if ($force) {
$acmesh_cmd .= " --force";
}
if (defined('CRON_DEBUG_FLAG')) {
$acmesh_cmd .= " --debug";
}
$acme_result = \Froxlor\FileDir::safe_exec($acmesh_cmd);
// debug output of acme.sh run
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, implode("\n", $acme_result));
$return = array();
self::readCertificateToVar($certrow['domain'], $return);
if (! empty($return['crt'])) {
$newcert = openssl_x509_parse($return['crt']);
if ($newcert) {
// Store the new data
Database::pexecute(self::$updcert_stmt, array(
'id' => $certrow['id'],
'domainid' => $certrow['domainid'],
'crt' => $return['crt'],
'key' => $return['key'],
'ca' => $return['chain'],
'chain' => $return['chain'],
'csr' => $return['csr'],
'fullchain' => $return['fullchain'],
'expirationdate' => date('Y-m-d H:i:s', $newcert['validTo_time_t'])
));
if ($certrow['ssl_redirect'] == 3) {
Database::pexecute(self::$upddom_stmt, array(
'domainid' => $certrow['domainid']
));
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Updated Let's Encrypt certificate for " . $certrow['domain']);
$changedetected = 1;
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Got non-successful Let's Encrypt response for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
}
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not get Let's Encrypt certificate for " . $certrow['domain'] . ":\n" . implode("\n", $acme_result));
}
}
}
private static function readCertificateToVar($domain, &$return)
{ {
$certificate_folder = dirname(self::$acmesh) . "/" . $domain; $certificate_folder = dirname(self::$acmesh) . "/" . $domain;
if (Settings::Get('system.leecc') > 0) { if (Settings::Get('system.leecc') > 0) {
$certificate_folder .= "_ecc"; $certificate_folder .= "_ecc";
} }
$certificate_folder = \Froxlor\FileDir::makeCorrectDir($certificate_folder); $certificate_folder = \Froxlor\FileDir::makeCorrectDir($certificate_folder);
$ssl_file = \Froxlor\FileDir::makeCorrectFile($certificate_folder . '/' . $domain . '.cer');
if (is_dir($certificate_folder)) { if (is_dir($certificate_folder) && file_exists($ssl_file) && is_readable($ssl_file)) {
$cert_data = openssl_x509_parse(file_get_contents($ssl_file));
if (strtotime($cert_data['validTo_time_t']) > strtotime($cert_date)) {
return true;
}
}
return false;
}
/**
* get certificate files from filesystem and store in $return array
*
* @param string $domain
* @param array $return
* @param object $cronlog
*/
private static function readCertificateToVar($domain, &$return, &$cronlog)
{
$certificate_folder = dirname(self::$acmesh) . "/" . $domain;
$certificate_folder_noecc = null;
if (Settings::Get('system.leecc') > 0) {
$certificate_folder_noecc = \Froxlor\FileDir::makeCorrectDir($certificate_folder);
$certificate_folder .= "_ecc";
}
$certificate_folder = \Froxlor\FileDir::makeCorrectDir($certificate_folder);
if (is_dir($certificate_folder) || is_dir($certificate_folder_noecc)) {
foreach ([ foreach ([
'crt' => $domain . '.cer', 'crt' => $domain . '.cer',
'key' => $domain . '.key', 'key' => $domain . '.key',
@@ -405,28 +535,52 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
if (file_exists($ssl_file)) { if (file_exists($ssl_file)) {
$return[$index] = file_get_contents($ssl_file); $return[$index] = file_get_contents($ssl_file);
} else { } else {
if (! empty($certificate_folder_noecc)) {
$ssl_file_fb = \Froxlor\FileDir::makeCorrectFile($certificate_folder_noecc . '/' . $sslfile);
if (file_exists($ssl_file_fb)) {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "ECC certificates activated but found only non-ecc file");
$return[$index] = file_get_contents($ssl_file_fb);
continue;
}
}
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find file '" . $sslfile . "' in '" . $certificate_folder . "'");
$return[$index] = null; $return[$index] = null;
} }
} }
} else {
$cronlog->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Could not find certificate-folder '" . $certificate_folder . "'");
} }
} }
private static function checkInstall() /**
* install acme.sh if not found yet
*/
private static function checkInstall($tries = 0)
{ {
if (! file_exists(self::$acmesh)) { if (! file_exists(self::$acmesh) && $tries > 0) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_ERR, "Download/installation of acme.sh seems to have failed. Re-run cronjob to try again or install manually to '" . self::$acmesh . "'");
echo PHP_EOL . "Download/installation of acme.sh seems to have failed. Re-run cronjob to try again or install manually to '" . self::$acmesh . "'" . PHP_EOL;
return false;
} else if (! file_exists(self::$acmesh)) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Could not find acme.sh - installing it to /root/.acme.sh/"); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Could not find acme.sh - installing it to /root/.acme.sh/");
$return = false; $return = false;
\Froxlor\FileDir::safe_exec("wget -O - https://get.acme.sh | sh", $return, array( \Froxlor\FileDir::safe_exec("wget -O - https://get.acme.sh | sh", $return, array(
'|' '|'
)); ));
// check whether the installation worked
return self::checkInstall(++ $tries);
} }
return true;
} }
/**
* run upgrade
*/
private static function checkUpgrade() private static function checkUpgrade()
{ {
$acmesh_result = \Froxlor\FileDir::safe_exec(self::$acmesh . " --upgrade"); $acmesh_result = \Froxlor\FileDir::safe_exec(self::$acmesh . " --upgrade");
// check for activated cron (which is installed automatically) but we don't need it // check for activated cron
$acmesh_result2 = \Froxlor\FileDir::safe_exec(self::$acmesh . " --uninstall-cronjob"); $acmesh_result2 = \Froxlor\FileDir::safe_exec(self::$acmesh . " --install-cronjob");
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Checking for LetsEncrypt client upgrades before renewing certificates:\n" . implode("\n", $acmesh_result) . "\n" . implode("\n", $acmesh_result2)); FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Checking for LetsEncrypt client upgrades before renewing certificates:\n" . implode("\n", $acmesh_result) . "\n" . implode("\n", $acmesh_result2));
} }
} }

View File

@@ -77,6 +77,7 @@ class BackupCron extends \Froxlor\Cron\FroxlorCron
$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :id"); $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_TASKS . "` WHERE `id` = :id");
$cronlog = FroxlorLogger::getInstanceOf();
$all_jobs = $result_tasks_stmt->fetchAll(); $all_jobs = $result_tasks_stmt->fetchAll();
foreach ($all_jobs as $row) { foreach ($all_jobs as $row) {
@@ -96,7 +97,7 @@ class BackupCron extends \Froxlor\Cron\FroxlorCron
\Froxlor\FileDir::safe_exec('mkdir -p ' . escapeshellarg($row['data']['destdir'])); \Froxlor\FileDir::safe_exec('mkdir -p ' . escapeshellarg($row['data']['destdir']));
} }
self::createCustomerBackup($row['data'], $customerdocroot, FroxlorLogger::getInstanceOf()); self::createCustomerBackup($row['data'], $customerdocroot, $cronlog);
} }
} }

View File

@@ -67,7 +67,7 @@ class Extrausers
'name' => \Froxlor\Customer\Customer::getCustomerDetail($u['customerid'], 'name'), 'name' => \Froxlor\Customer\Customer::getCustomerDetail($u['customerid'], 'name'),
'company' => \Froxlor\Customer\Customer::getCustomerDetail($u['customerid'], 'company') 'company' => \Froxlor\Customer\Customer::getCustomerDetail($u['customerid'], 'company')
); );
$u['comment'] = \Froxlor\User::getCorrectUserSalutation($salutation_array); $u['comment'] = self::cleanString(\Froxlor\User::getCorrectUserSalutation($salutation_array));
if ($u['login_enabled'] != 'Y') { if ($u['login_enabled'] != 'Y') {
$u['password'] = '*'; $u['password'] = '*';
$u['shell'] = '/bin/false'; $u['shell'] = '/bin/false';
@@ -90,4 +90,10 @@ class Extrausers
$cronlog->logAction(\Froxlor\FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Error when writing ' . $type . ' file entries'); $cronlog->logAction(\Froxlor\FroxlorLogger::CRON_ACTION, LOG_NOTICE, 'Error when writing ' . $type . ' file entries');
} }
} }
private static function cleanString($string = null)
{
$allowed = "/[^a-z0-9\\.\\-\\_\\ ]/i";
return preg_replace($allowed, "", $string);
}
} }

View File

@@ -36,7 +36,7 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
if ((int) Settings::Get('system.report_trafficmax') > 0) { if ((int) Settings::Get('system.report_trafficmax') > 0) {
// Warn the customers at xx% traffic-usage // Warn the customers at xx% traffic-usage
$result_stmt = Database::prepare(" $result_stmt = Database::prepare("
SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, SELECT `c`.`customerid`, `c`.`customernumber`, `c`.`adminid`, `c`.`name`, `c`.`firstname`,
`c`.`company`, `c`.`traffic`, `c`.`email`, `c`.`def_language`, `c`.`company`, `c`.`traffic`, `c`.`email`, `c`.`def_language`,
`a`.`name` AS `adminname`, `a`.`email` AS `adminmail`, `a`.`name` AS `adminname`, `a`.`email` AS `adminmail`,
(SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`) (SELECT SUM(`t`.`http` + `t`.`ftp_up` + `t`.`ftp_down` + `t`.`mail`)
@@ -55,16 +55,19 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
Database::pexecute($result_stmt, $result_data); Database::pexecute($result_stmt, $result_data);
while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) { while ($row = $result_stmt->fetch(\PDO::FETCH_ASSOC)) {
if (isset($row['traffic']) && $row['traffic'] > 0 && $row['traffic_used'] != null && (($row['traffic_used'] * 100) / $row['traffic']) >= (int) Settings::Get('system.report_trafficmax')) { if (isset($row['traffic']) && $row['traffic'] > 0 && $row['traffic_used'] != null && (($row['traffic_used'] * 100) / $row['traffic']) >= (int) Settings::Get('system.report_trafficmax')) {
$rep_userinfo = array( $rep_userinfo = array(
'name' => $row['name'], 'name' => $row['name'],
'firstname' => $row['firstname'], 'firstname' => $row['firstname'],
'company' => $row['company'] 'company' => $row['company'],
'customernumber' => $row['customernumber']
); );
$replace_arr = array( $replace_arr = array(
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($rep_userinfo), 'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($rep_userinfo),
'NAME' => $row['name'], // < keep this for compatibility 'NAME' => $rep_userinfo['name'],
'FIRSTNAME' => $rep_userinfo['firstname'],
'COMPANY' => $rep_userinfo['company'],
'CUSTOMER_NO' => $rep_userinfo['customernumber'],
'TRAFFIC' => round(($row['traffic'] / 1024), 2), /* traffic is stored in KB, template uses MB */ 'TRAFFIC' => round(($row['traffic'] / 1024), 2), /* traffic is stored in KB, template uses MB */
'TRAFFICUSED' => round(($row['traffic_used'] / 1024), 2), /* traffic is stored in KB, template uses MB */ 'TRAFFICUSED' => round(($row['traffic_used'] / 1024), 2), /* traffic is stored in KB, template uses MB */
'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2), 'USAGE_PERCENT' => round(($row['traffic_used'] * 100) / $row['traffic'], 2),
@@ -89,9 +92,11 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
} }
// include english language file (fallback) // include english language file (fallback)
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php'); include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php');
// include admin/customer language file // include admin/customer language file
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile); if ($lngfile != 'lng/english.lng.php') {
include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile);
}
// Get mail templates from database; the ones from 'admin' are fetched for fallback // Get mail templates from database; the ones from 'admin' are fetched for fallback
$result2_stmt = Database::prepare(" $result2_stmt = Database::prepare("
@@ -142,6 +147,8 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
Database::pexecute($upd_stmt, array( Database::pexecute($upd_stmt, array(
'customerid' => $row['customerid'] 'customerid' => $row['customerid']
)); ));
unset($lng);
} }
} }
@@ -168,8 +175,8 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
$replace_arr = array( $replace_arr = array(
'NAME' => $row['name'], 'NAME' => $row['name'],
'TRAFFIC' => round(($row['traffic'] / 1024), 2), /* traffic is stored in KB, template uses MB */ 'TRAFFIC' => round(($row['traffic'] / 1024), 2), /* traffic is stored in KB, template uses MB */
'TRAFFICUSED' => round(($row['traffic_used_total'] / 1024), 2), /* traffic is stored in KB, template uses MB */ 'TRAFFICUSED' => round(($row['traffic_used_total'] / 1024), 2), /* traffic is stored in KB, template uses MB */
'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2), 'USAGE_PERCENT' => round(($row['traffic_used_total'] * 100) / $row['traffic'], 2),
'MAX_PERCENT' => Settings::Get('system.report_trafficmax') 'MAX_PERCENT' => Settings::Get('system.report_trafficmax')
); );
@@ -191,9 +198,11 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
} }
// include english language file (fallback) // include english language file (fallback)
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php'); include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php');
// include admin/customer language file // include admin/customer language file
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile); if ($lngfile != 'lng/english.lng.php') {
include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile);
}
// Get mail templates from database; the ones from 'admin' are fetched for fallback // Get mail templates from database; the ones from 'admin' are fetched for fallback
$result2_stmt = Database::prepare(" $result2_stmt = Database::prepare("
@@ -322,6 +331,8 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
} }
$mail->ClearAddresses(); $mail->ClearAddresses();
unset($lng);
} }
} }
} // trafficmax > 0 } // trafficmax > 0
@@ -343,7 +354,7 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
* report about diskusage for customers * report about diskusage for customers
*/ */
$result_stmt = Database::query(" $result_stmt = Database::query("
SELECT `c`.`customerid`, `c`.`adminid`, `c`.`name`, `c`.`firstname`, SELECT `c`.`customerid`, `c`.`customernumber`, `c`.`adminid`, `c`.`name`, `c`.`firstname`,
`c`.`company`, `c`.`diskspace`, `c`.`diskspace_used`, `c`.`email`, `c`.`def_language`, `c`.`company`, `c`.`diskspace`, `c`.`diskspace_used`, `c`.`email`, `c`.`def_language`,
`a`.`name` AS `adminname`, `a`.`email` AS `adminmail` `a`.`name` AS `adminname`, `a`.`email` AS `adminmail`
FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c` FROM `" . TABLE_PANEL_CUSTOMERS . "` AS `c`
@@ -361,11 +372,15 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
$rep_userinfo = array( $rep_userinfo = array(
'name' => $row['name'], 'name' => $row['name'],
'firstname' => $row['firstname'], 'firstname' => $row['firstname'],
'company' => $row['company'] 'company' => $row['company'],
'customernumber' => $row['customernumber']
); );
$replace_arr = array( $replace_arr = array(
'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($rep_userinfo), 'SALUTATION' => \Froxlor\User::getCorrectUserSalutation($rep_userinfo),
'NAME' => $row['name'], // < keep this for compatibility 'NAME' => $rep_userinfo['name'],
'FIRSTNAME' => $rep_userinfo['firstname'],
'COMPANY' => $rep_userinfo['company'],
'CUSTOMER_NO' => $rep_userinfo['customernumber'],
'DISKAVAILABLE' => round(($row['diskspace'] / 1024), 2), /* traffic is stored in KB, template uses MB */ 'DISKAVAILABLE' => round(($row['diskspace'] / 1024), 2), /* traffic is stored in KB, template uses MB */
'DISKUSED' => round($row['diskspace_used'] / 1024, 2), /* traffic is stored in KB, template uses MB */ 'DISKUSED' => round($row['diskspace_used'] / 1024, 2), /* traffic is stored in KB, template uses MB */
'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2), 'USAGE_PERCENT' => round(($row['diskspace_used'] * 100) / $row['diskspace'], 2),
@@ -386,13 +401,15 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
$lngfile = Database::pexecute_first($lngfile_stmt, array( $lngfile = Database::pexecute_first($lngfile_stmt, array(
'deflang' => Settings::Get('panel.standardlanguage') 'deflang' => Settings::Get('panel.standardlanguage')
)); ));
$langfile = $lngfile['file']; $langfile = $lngfile['file'] ?? 'lng/english.lng.php';
} }
// include english language file (fallback) // include english language file (fallback)
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php'); include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php');
// include admin/customer language file // include admin/customer language file
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile); if ($lngfile != 'lng/english.lng.php') {
include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile);
}
// Get mail templates from database; the ones from 'admin' are fetched for fallback // Get mail templates from database; the ones from 'admin' are fetched for fallback
$result2_stmt = Database::prepare(" $result2_stmt = Database::prepare("
@@ -443,6 +460,8 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
Database::pexecute($upd_stmt, array( Database::pexecute($upd_stmt, array(
'customerid' => $row['customerid'] 'customerid' => $row['customerid']
)); ));
unset($lng);
} }
} }
@@ -483,9 +502,11 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
} }
// include english language file (fallback) // include english language file (fallback)
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php'); include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/lng/english.lng.php');
// include admin/customer language file // include admin/customer language file
include_once \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile); if ($lngfile != 'lng/english.lng.php') {
include \Froxlor\FileDir::makeCorrectFile(\Froxlor\Froxlor::getInstallDir() . '/' . $langfile);
}
// Get mail templates from database; the ones from 'admin' are fetched for fallback // Get mail templates from database; the ones from 'admin' are fetched for fallback
$result2_stmt = Database::prepare(" $result2_stmt = Database::prepare("
@@ -536,6 +557,8 @@ class ReportsCron extends \Froxlor\Cron\FroxlorCron
Database::pexecute($upd_stmt, array( Database::pexecute($upd_stmt, array(
'adminid' => $row['adminid'] 'adminid' => $row['adminid']
)); ));
unset($lng);
} }
} }
} // webmax > 0 } // webmax > 0

View File

@@ -6,6 +6,41 @@ use Froxlor\Database\Database;
class Domain class Domain
{ {
/**
* return all ip addresses associated with given domain,
* returns all ips if domain-id = 0 (froxlor.vhost)
*
* @param int $domain_id
* @return array
*/
public static function getIpsOfDomain($domain_id)
{
if ($domain_id > 0) {
$sel_stmt = Database::prepare("
SELECT i.ip FROM `" . TABLE_PANEL_IPSANDPORTS . "` `i`
LEFT JOIN `" . TABLE_DOMAINTOIP . "` `dip` ON dip.id_ipandports = i.id
AND dip.id_domain = :domainid
GROUP BY i.ip
");
$sel_param = array(
'domainid' => $domain_id
);
} else {
// assuming froxlor.vhost (id = 0)
$sel_stmt = Database::prepare("
SELECT ip FROM `" . TABLE_PANEL_IPSANDPORTS . "`
GROUP BY ip
");
$sel_param = array();
}
Database::pexecute($sel_stmt, $sel_param);
$result = array();
while ($ip = $sel_stmt->fetch(\PDO::FETCH_ASSOC)) {
$result[] = $ip['ip'];
}
return $result;
}
/** /**
* return an array of all enabled redirect-codes * return an array of all enabled redirect-codes
* *

View File

@@ -7,10 +7,10 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '0.10.13'; const VERSION = '0.10.17';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '201912313'; const DBVERSION = '202005150';
// Distribution branding-tag (used for Debian etc.) // Distribution branding-tag (used for Debian etc.)
const BRANDING = ''; const BRANDING = '';

View File

@@ -209,12 +209,12 @@ class MailLogParser
$timestamp = $this->getLogTimestamp($line); $timestamp = $this->getLogTimestamp($line);
if ($this->startTime < $timestamp) { if ($this->startTime < $timestamp) {
if (preg_match("/dovecot.*(?::|\]) imap\(.*@([a-z0-9\.\-]+)\):.*(?:in=(\d+) out=(\d+)|bytes=(\d+)\/(\d+))/i", $line, $matches)) { if (preg_match("/dovecot.*(?::|\]) imap\(.*@([a-z0-9\.\-]+)\)(<\d+><[a-z0-9+\/=]+>)?:.*(?:in=(\d+) out=(\d+)|bytes=(\d+)\/(\d+))/i", $line, $matches)) {
// Dovecot IMAP // Dovecot IMAP
$this->addDomainTraffic($matches[1], (int) $matches[2] + (int) $matches[3], $timestamp); $this->addDomainTraffic($matches[1], (int) $matches[3] + (int) $matches[4], $timestamp);
} elseif (preg_match("/dovecot.*(?::|\]) pop3\(.*@([a-z0-9\.\-]+)\):.*in=(\d+).*out=(\d+)/i", $line, $matches)) { } elseif (preg_match("/dovecot.*(?::|\]) pop3\(.*@([a-z0-9\.\-]+)\)(<\d+><[a-z0-9+\/=]+>)?:.*in=(\d+).*out=(\d+)/i", $line, $matches)) {
// Dovecot POP3 // Dovecot POP3
$this->addDomainTraffic($matches[1], (int) $matches[2] + (int) $matches[3], $timestamp); $this->addDomainTraffic($matches[1], (int) $matches[3] + (int) $matches[4], $timestamp);
} }
} }
} }

View File

@@ -384,7 +384,7 @@ return array(
'value' => array() 'value' => array()
), ),
'sessiontickets' => array( 'sessiontickets' => array(
'visible' => ($ssl_ipsandports != '' ? true : false) && \Froxlor\Settings::Get('system.webserver') != 'lighttpd', 'visible' => ($ssl_ipsandports != '' ? true : false) && \Froxlor\Settings::Get('system.webserver') != 'lighttpd' && \Froxlor\Settings::Get('system.sessionticketsenabled' != '1'),
'label' => $lng['admin']['domain_sessiontickets'], 'label' => $lng['admin']['domain_sessiontickets'],
'type' => 'checkbox', 'type' => 'checkbox',
'values' => array( 'values' => array(

View File

@@ -431,7 +431,7 @@ return array(
) )
), ),
'sessiontickets' => array( 'sessiontickets' => array(
'visible' => ($ssl_ipsandports != '' ? true : false) && \Froxlor\Settings::Get('system.webserver') != 'lighttpd', 'visible' => ($ssl_ipsandports != '' ? true : false) && \Froxlor\Settings::Get('system.webserver') != 'lighttpd' && \Froxlor\Settings::Get('system.sessionticketsenabled' != '1'),
'label' => $lng['admin']['domain_sessiontickets'], 'label' => $lng['admin']['domain_sessiontickets'],
'type' => 'checkbox', 'type' => 'checkbox',
'values' => array( 'values' => array(

View File

@@ -20,6 +20,17 @@
// define default theme for configurehint, etc. // define default theme for configurehint, etc.
$_deftheme = 'Sparkle'; $_deftheme = 'Sparkle';
// validate correct php version
if (version_compare("7.0.0", PHP_VERSION, ">=")) {
// get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/phprequirementfailed.tpl');
// replace values
$vendor_hint = str_replace("<FROXLOR_PHPMIN>", "7.0.0", $vendor_hint);
$vendor_hint = str_replace("<CURRENT_VERSION>", PHP_VERSION, $vendor_hint);
$vendor_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $vendor_hint);
die($vendor_hint);
}
if (! file_exists(dirname(__DIR__) . '/vendor/autoload.php')) { if (! file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
// get hint-template // get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/vendormissinghint.tpl'); $vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/vendormissinghint.tpl');

View File

@@ -204,6 +204,7 @@ $lng['error']['mydomain'] = '\'Domain\'';
$lng['error']['mydocumentroot'] = '\'Documentroot\''; $lng['error']['mydocumentroot'] = '\'Documentroot\'';
$lng['error']['loginnameexists'] = 'Loginname %s already exists'; $lng['error']['loginnameexists'] = 'Loginname %s already exists';
$lng['error']['emailiswrong'] = 'Email-address %s contains invalid characters or is incomplete'; $lng['error']['emailiswrong'] = 'Email-address %s contains invalid characters or is incomplete';
$lng['error']['alternativeemailiswrong'] = 'The given alternative email address %s to send the credentials to seems to be invalid';
$lng['error']['loginnameiswrong'] = 'Loginname "%s" contains illegal characters.'; $lng['error']['loginnameiswrong'] = 'Loginname "%s" contains illegal characters.';
$lng['error']['loginnameiswrong2'] = 'Loginname contains too many characters. Only %s characters are allowed.'; $lng['error']['loginnameiswrong2'] = 'Loginname contains too many characters. Only %s characters are allowed.';
$lng['error']['userpathcombinationdupe'] = 'Combination of username and path already exists'; $lng['error']['userpathcombinationdupe'] = 'Combination of username and path already exists';
@@ -317,6 +318,7 @@ $lng['admin']['templates']['COMPANY'] = 'Replaces with the customer\'s company n
$lng['admin']['templates']['USERNAME'] = 'Replaced with the customer\'s account username.'; $lng['admin']['templates']['USERNAME'] = 'Replaced with the customer\'s account username.';
$lng['admin']['templates']['PASSWORD'] = 'Replaced with the customer\'s account password.'; $lng['admin']['templates']['PASSWORD'] = 'Replaced with the customer\'s account password.';
$lng['admin']['templates']['EMAIL'] = 'Replaced with the address of the POP3/IMAP account.'; $lng['admin']['templates']['EMAIL'] = 'Replaced with the address of the POP3/IMAP account.';
$lng['admin']['templates']['CUSTOMER_NO'] = 'Replaces with the customer number';
$lng['admin']['webserver'] = 'Webserver'; $lng['admin']['webserver'] = 'Webserver';
$lng['admin']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '<br /><strong class="red">ATTENTION:</strong> If you use a zonefile you will have to manage all required records for all sub-zones manually as well.'; $lng['admin']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '<br /><strong class="red">ATTENTION:</strong> If you use a zonefile you will have to manage all required records for all sub-zones manually as well.';
@@ -408,6 +410,7 @@ $lng['admin']['ipsandports']['add'] = 'Add IP/Port';
$lng['admin']['ipsandports']['edit'] = 'Edit IP/Port'; $lng['admin']['ipsandports']['edit'] = 'Edit IP/Port';
$lng['admin']['ipsandports']['ipandport'] = 'IP/Port'; $lng['admin']['ipsandports']['ipandport'] = 'IP/Port';
$lng['admin']['ipsandports']['ip'] = 'IP'; $lng['admin']['ipsandports']['ip'] = 'IP';
$lng['admin']['ipsandports']['ipnote'] = '<div id="ipnote" class="red">Note: Although private ip addresses are allowed, some features like DNS might not behave correctly.<br>Only use private ip addresses if you are sure.</div>';
$lng['admin']['ipsandports']['port'] = 'Port'; $lng['admin']['ipsandports']['port'] = 'Port';
// ADDED IN 1.2.13-rc3 // ADDED IN 1.2.13-rc3
@@ -1695,7 +1698,7 @@ $lng['admin']['integrityresult'] = 'Result';
$lng['admin']['integrityfix'] = 'Fix problems automatically'; $lng['admin']['integrityfix'] = 'Fix problems automatically';
$lng['question']['admin_integritycheck_reallyfix'] = 'Do you really want to try fixing all database integrity problems automatically?'; $lng['question']['admin_integritycheck_reallyfix'] = 'Do you really want to try fixing all database integrity problems automatically?';
$lng['serversettings']['system_croncmdline']['title'] = 'Cron execution command (php-binary)'; $lng['serversettings']['system_croncmdline']['title'] = 'Cron execution command (php-binary)';
$lng['serversettings']['system_croncmdline']['description'] = 'Command to execute our cronjobs. Change this only if you know what you are doing (default: "/usr/bin/nice -n 5 /usr/bin/php5 -q")!'; $lng['serversettings']['system_croncmdline']['description'] = 'Command to execute our cronjobs. Change this only if you know what you are doing (default: "/usr/bin/nice -n 5 /usr/bin/php -q")!';
$lng['error']['cannotdeletehostnamephpconfig'] = 'This PHP-configuration is used by the Froxlor-vhost and cannot be deleted.'; $lng['error']['cannotdeletehostnamephpconfig'] = 'This PHP-configuration is used by the Froxlor-vhost and cannot be deleted.';
$lng['error']['cannotdeletedefaultphpconfig'] = 'This PHP-configuration is set as default and cannot be deleted.'; $lng['error']['cannotdeletedefaultphpconfig'] = 'This PHP-configuration is set as default and cannot be deleted.';
$lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Allow automatic database updates'; $lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Allow automatic database updates';
@@ -1980,8 +1983,8 @@ $lng['admin']['domain_http2']['title'] = 'HTTP2 support';
$lng['admin']['domain_http2']['description'] = 'See <a target="_blank" href="https://en.wikipedia.org/wiki/HTTP/2">Wikipedia</a> for a detailed explanation of HTTP2'; $lng['admin']['domain_http2']['description'] = 'See <a target="_blank" href="https://en.wikipedia.org/wiki/HTTP/2">Wikipedia</a> for a detailed explanation of HTTP2';
$lng['admin']['testmail'] = 'SMTP test'; $lng['admin']['testmail'] = 'SMTP test';
$lng['success']['testmailsent'] = 'Test mail sent successfully'; $lng['success']['testmailsent'] = 'Test mail sent successfully';
$lng['serversettings']['disable_le_selfcheck']['title'] = "Disable Let's Encrypt local self-check"; $lng['serversettings']['le_domain_dnscheck']['title'] = "Validate DNS of domains when using Let's Encrypt";
$lng['serversettings']['disable_le_selfcheck']['description'] = "If activated, froxlor will <strong>not</strong> perform its self-check for token accessibility. Needed for NATed IP's or similar."; $lng['serversettings']['le_domain_dnscheck']['description'] = "If activated, froxlor will validate whether the domain which requests a Let's Encrypt certificate resolves to at least one of the system ip addresses.";
$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions'; $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM versions';
$lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In use for php-config(s)';
$lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:'; $lng['admin']['phpsettingsforsubdomains'] = 'Apply php-config to all subdomains:';

View File

@@ -202,6 +202,7 @@ $lng['error']['mydomain'] = '\'Domain\'';
$lng['error']['mydocumentroot'] = '\'Documentroot\''; $lng['error']['mydocumentroot'] = '\'Documentroot\'';
$lng['error']['loginnameexists'] = 'Der Login-Name "%s" existiert bereits.'; $lng['error']['loginnameexists'] = 'Der Login-Name "%s" existiert bereits.';
$lng['error']['emailiswrong'] = 'Die E-Mail-Adresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.'; $lng['error']['emailiswrong'] = 'Die E-Mail-Adresse "%s" enthält ungültige Zeichen oder ist nicht vollständig.';
$lng['error']['alternativeemailiswrong'] = 'Die angegebene alternative E-Mail Adresse "%s", an welche die Zugangsdaten geschickt werden soll, scheint ungültig zu sein.';
$lng['error']['loginnameiswrong'] = 'Der Login-Name "%s" enthält ungültige Zeichen.'; $lng['error']['loginnameiswrong'] = 'Der Login-Name "%s" enthält ungültige Zeichen.';
$lng['error']['loginnameiswrong2'] = 'Der Login-Name enthält zu viele Zeichen, es sind maximal %s Zeichen erlaubt.'; $lng['error']['loginnameiswrong2'] = 'Der Login-Name enthält zu viele Zeichen, es sind maximal %s Zeichen erlaubt.';
$lng['error']['userpathcombinationdupe'] = 'Die Kombination aus Benutzername und Pfad existiert bereits.'; $lng['error']['userpathcombinationdupe'] = 'Die Kombination aus Benutzername und Pfad existiert bereits.';
@@ -314,6 +315,7 @@ $lng['admin']['templates']['COMPANY'] = 'Wird mit dem Firmennamen des Kunden ers
$lng['admin']['templates']['USERNAME'] = 'Wird mit dem Benutzernamen des neuen Kundenkontos ersetzt.'; $lng['admin']['templates']['USERNAME'] = 'Wird mit dem Benutzernamen des neuen Kundenkontos ersetzt.';
$lng['admin']['templates']['PASSWORD'] = 'Wird mit dem Passwort des neuen Kundenkontos ersetzt.'; $lng['admin']['templates']['PASSWORD'] = 'Wird mit dem Passwort des neuen Kundenkontos ersetzt.';
$lng['admin']['templates']['EMAIL'] = 'Wird mit der Adresse des neuen E-Mail-Kontos ersetzt.'; $lng['admin']['templates']['EMAIL'] = 'Wird mit der Adresse des neuen E-Mail-Kontos ersetzt.';
$lng['admin']['templates']['CUSTOMER_NO'] = 'Wir mit der Kunden-Nummer ersetzt';
$lng['admin']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '<br /><strong class="red">WARNUNG:</strong> Bei der Verwendung einer Zonendatei müssen alle benötigten Records aller Subdomains ebenfalls manuell verwaltet werden.'; $lng['admin']['bindzonewarning'] = $lng['panel']['emptyfordefault'] . '<br /><strong class="red">WARNUNG:</strong> Bei der Verwendung einer Zonendatei müssen alle benötigten Records aller Subdomains ebenfalls manuell verwaltet werden.';
/** /**
@@ -403,6 +405,7 @@ $lng['admin']['ipsandports']['add'] = 'IP-Adresse/Port hinzufügen';
$lng['admin']['ipsandports']['edit'] = 'IP-Adresse/Port bearbeiten'; $lng['admin']['ipsandports']['edit'] = 'IP-Adresse/Port bearbeiten';
$lng['admin']['ipsandports']['ipandport'] = 'IP-Adresse/Port'; $lng['admin']['ipsandports']['ipandport'] = 'IP-Adresse/Port';
$lng['admin']['ipsandports']['ip'] = 'IP-Adresse'; $lng['admin']['ipsandports']['ip'] = 'IP-Adresse';
$lng['admin']['ipsandports']['ipnote'] = '<div id="ipnote" class="red">Hinweis: Obwohl private IP Adressen erlaubt sind, kann es bei manchen Features wie DNS zu ungewolltem Verhalten kommen.<br>Verwende private Adressen nur wenn du sicher bist.</div>';
$lng['admin']['ipsandports']['port'] = 'Port'; $lng['admin']['ipsandports']['port'] = 'Port';
// ADDED IN 1.2.13-rc3 // ADDED IN 1.2.13-rc3
@@ -1419,7 +1422,7 @@ $lng['admin']['integrityresult'] = 'Ergebnis';
$lng['admin']['integrityfix'] = 'Probleme automatisch beheben'; $lng['admin']['integrityfix'] = 'Probleme automatisch beheben';
$lng['question']['admin_integritycheck_reallyfix'] = 'M&ouml;chten Sie wirklich versuchen s&auml;mtliche Datenbank-Integrit&auml;tsprobleme automatisch zu beheben?'; $lng['question']['admin_integritycheck_reallyfix'] = 'M&ouml;chten Sie wirklich versuchen s&auml;mtliche Datenbank-Integrit&auml;tsprobleme automatisch zu beheben?';
$lng['serversettings']['system_croncmdline']['title'] = 'Cron Startbefehl (php Programm)'; $lng['serversettings']['system_croncmdline']['title'] = 'Cron Startbefehl (php Programm)';
$lng['serversettings']['system_croncmdline']['description'] = 'Befehl zum Ausführen des Cronjobs. Ändern dieser Einstellung nur wenn nötig (Standard: "/usr/bin/nice -n 5 /usr/bin/php5 -q")!'; $lng['serversettings']['system_croncmdline']['description'] = 'Befehl zum Ausführen des Cronjobs. Ändern dieser Einstellung nur wenn nötig (Standard: "/usr/bin/nice -n 5 /usr/bin/php -q")!';
$lng['error']['cannotdeletehostnamephpconfig'] = 'Diese PHP-Konfiguration ist dem Froxlor-Vhost zugewiesen und kann daher nicht gelöscht werden.'; $lng['error']['cannotdeletehostnamephpconfig'] = 'Diese PHP-Konfiguration ist dem Froxlor-Vhost zugewiesen und kann daher nicht gelöscht werden.';
$lng['error']['cannotdeletedefaultphpconfig'] = 'Diese PHP-Konfiguration ist als Standard hinterlegt und kann daher nicht gelöscht werden.'; $lng['error']['cannotdeletedefaultphpconfig'] = 'Diese PHP-Konfiguration ist als Standard hinterlegt und kann daher nicht gelöscht werden.';
$lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Erlaube automatische Datenbank-Aktualisierungen'; $lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Erlaube automatische Datenbank-Aktualisierungen';
@@ -1630,8 +1633,8 @@ $lng['admin']['domain_http2']['title'] = 'HTTP2 Unterstützung';
$lng['admin']['domain_http2']['description'] = 'Siehe <a target="_blank" href="https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP.2F2">Wikipedia</a> für eine ausführliche Beschreibung von HTTP2'; $lng['admin']['domain_http2']['description'] = 'Siehe <a target="_blank" href="https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP.2F2">Wikipedia</a> für eine ausführliche Beschreibung von HTTP2';
$lng['admin']['testmail'] = 'SMTP Test'; $lng['admin']['testmail'] = 'SMTP Test';
$lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet'; $lng['success']['testmailsent'] = 'Test E-Mail erfolgreich gesendet';
$lng['serversettings']['disable_le_selfcheck']['title'] = "Deaktiviere Let's Encrypt lokale Selbstprüfung"; $lng['serversettings']['le_domain_dnscheck']['title'] = "Validiere DNS der Domains wenn Let's Encrypt genutzt wird";
$lng['serversettings']['disable_le_selfcheck']['description'] = "Wenn aktiviert wird Froxlor <strong>keine</strong> Erreichbarkeitsprüfung des Tokens vornehmen. Nötig bei ge-NAT-eten IP-Adressen oder Ähnlichem"; $lng['serversettings']['le_domain_dnscheck']['description'] = "Wenn aktiviert wird froxlor überprüfen ob die DNS Einträge der Domains, welche ein Let's Encrypt Zertifikat beantragt, mindestens auf eine der System IP Adressen auflöst.";
$lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen'; $lng['menue']['phpsettings']['fpmdaemons'] = 'PHP-FPM Versionen';
$lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)'; $lng['admin']['phpsettings']['activephpconfigs'] = 'In Verwendung für PHP-Konfiguration(en)';
$lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:'; $lng['admin']['phpsettingsforsubdomains'] = 'PHP-Config für alle Subdomains übernehmen:';

View File

@@ -1635,7 +1635,7 @@ $lng['admin']['integrityresult'] = 'Risultato';
$lng['admin']['integrityfix'] = 'Risolvi problemi automaticamente'; $lng['admin']['integrityfix'] = 'Risolvi problemi automaticamente';
$lng['question']['admin_integritycheck_reallyfix'] = 'Vuoi veramente provare a risolvere i problemi di integrità del database automaticamente?'; $lng['question']['admin_integritycheck_reallyfix'] = 'Vuoi veramente provare a risolvere i problemi di integrità del database automaticamente?';
$lng['serversettings']['system_croncmdline']['title'] = 'Commando di esecuzione Cron (binario php)'; $lng['serversettings']['system_croncmdline']['title'] = 'Commando di esecuzione Cron (binario php)';
$lng['serversettings']['system_croncmdline']['description'] = 'Commando per eseguire i nostri cronjob. Modificalo soltanto se sai cosa stai facendo (predefinito: "/usr/bin/nice -n 5 /usr/bin/php5 -q")!'; $lng['serversettings']['system_croncmdline']['description'] = 'Commando per eseguire i nostri cronjob. Modificalo soltanto se sai cosa stai facendo (predefinito: "/usr/bin/nice -n 5 /usr/bin/php -q")!';
$lng['error']['cannotdeletehostnamephpconfig'] = 'Questa configurazione PHP è utilizzata dal vhost Froxlor e non può essere eliminata.'; $lng['error']['cannotdeletehostnamephpconfig'] = 'Questa configurazione PHP è utilizzata dal vhost Froxlor e non può essere eliminata.';
$lng['error']['cannotdeletedefaultphpconfig'] = 'Questa configurazione PHP è impostata come predefinita e non può essere eliminata.'; $lng['error']['cannotdeletedefaultphpconfig'] = 'Questa configurazione PHP è impostata come predefinita e non può essere eliminata.';
$lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Permetti aggiornamenti automatici del database'; $lng['serversettings']['system_cron_allowautoupdate']['title'] = 'Permetti aggiornamenti automatici del database';

View File

@@ -14,6 +14,12 @@
* @package Cron * @package Cron
* *
*/ */
// validate correct php version
if (version_compare("7.0.0", PHP_VERSION, ">=")) {
die('Froxlor requires at least php-7.0. Please validate that your php-cli version and the cron execution command are correct.');
}
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
\Froxlor\Cron\MasterCron::setArguments($argv); \Froxlor\Cron\MasterCron::setArguments($argv);

View File

@@ -30,7 +30,7 @@
<table class="full hl"> <table class="full hl">
<thead> <thead>
<tr> <tr>
<th>{$lng['domains']['domainname']}&nbsp;{$arrowcode['d.domain']}</th> <th>{$lng['domains']['domainname']}&nbsp;{$arrowcode['d.domain_ace']}</th>
<th>{$lng['admin']['ipsandports']['ip']}</th> <th>{$lng['admin']['ipsandports']['ip']}</th>
<th>{$lng['admin']['customer']}&nbsp;{$arrowcode['c.loginname']}</th> <th>{$lng['admin']['customer']}&nbsp;{$arrowcode['c.loginname']}</th>
<th>{$lng['panel']['options']}</th> <th>{$lng['panel']['options']}</th>

View File

@@ -6,6 +6,7 @@ $header
{$title} {$title}
</h2> </h2>
</header> </header>
<script type="text/javascript" src="templates/{$theme}/assets/js/ipsandports.js"></script>
<section> <section>

View File

@@ -6,6 +6,7 @@ $header
{$title} {$title}
</h2> </h2>
</header> </header>
<script type="text/javascript" src="templates/{$theme}/assets/js/ipsandports.js"></script>
<section> <section>

View File

@@ -40,23 +40,27 @@ $header
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr>
<td><em>{FIRSTNAME}</em></td>
<td>{$lng['admin']['templates']['FIRSTNAME']}</td>
</tr>
<tr>
<td><em>{NAME}</em></td>
<td>{$lng['admin']['templates']['NAME']}</td>
</tr>
<tr>
<td><em>{COMPANY}</em></td>
<td>{$lng['admin']['templates']['COMPANY']}</td>
</tr>
<tr>
<td><em>{CUSTOMER_NO}</em></td>
<td>{$lng['admin']['templates']['CUSTOMER_NO']}</td>
</tr>
<if ($template == 'createcustomer')> <if ($template == 'createcustomer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr>
<td><em>{FIRSTNAME}</em></td>
<td>{$lng['admin']['templates']['FIRSTNAME']}</td>
</tr>
<tr>
<td><em>{NAME}</em></td>
<td>{$lng['admin']['templates']['NAME']}</td>
</tr>
<tr>
<td><em>{COMPANY}</em></td>
<td>{$lng['admin']['templates']['COMPANY']}</td>
</tr>
<tr> <tr>
<td><em>{USERNAME}</em></td> <td><em>{USERNAME}</em></td>
<td>{$lng['admin']['templates']['USERNAME']}</td> <td>{$lng['admin']['templates']['USERNAME']}</td>
@@ -88,10 +92,6 @@ $header
</if> </if>
</if> </if>
<if ($template == 'password_reset')> <if ($template == 'password_reset')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{USERNAME}</em></td> <td><em>{USERNAME}</em></td>
<td>{$lng['admin']['templates']['USERNAME']}</td> <td>{$lng['admin']['templates']['USERNAME']}</td>
@@ -138,10 +138,6 @@ $header
</tr> </tr>
</if> </if>
<if ($template == 'new_database_by_customer')> <if ($template == 'new_database_by_customer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{DB_NAME}</em></td> <td><em>{DB_NAME}</em></td>
<td>{$lng['admin']['templates']['DB_NAME']}</td> <td>{$lng['admin']['templates']['DB_NAME']}</td>
@@ -164,10 +160,6 @@ $header
</tr> </tr>
</if> </if>
<if ($template == 'new_ftpaccount_by_customer')> <if ($template == 'new_ftpaccount_by_customer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{USR_NAME}</em></td> <td><em>{USR_NAME}</em></td>
<td>{$lng['admin']['templates']['USR_NAME']}</td> <td>{$lng['admin']['templates']['USR_NAME']}</td>

View File

@@ -42,23 +42,27 @@ $header
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr>
<td><em>{FIRSTNAME}</em></td>
<td>{$lng['admin']['templates']['FIRSTNAME']}</td>
</tr>
<tr>
<td><em>{NAME}</em></td>
<td>{$lng['admin']['templates']['NAME']}</td>
</tr>
<tr>
<td><em>{COMPANY}</em></td>
<td>{$lng['admin']['templates']['COMPANY']}</td>
</tr>
<tr>
<td><em>{CUSTOMER_NO}</em></td>
<td>{$lng['admin']['templates']['CUSTOMER_NO']}</td>
</tr>
<if ($template_name == 'createcustomer')> <if ($template_name == 'createcustomer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr>
<td><em>{FIRSTNAME}</em></td>
<td>{$lng['admin']['templates']['FIRSTNAME']}</td>
</tr>
<tr>
<td><em>{NAME}</em></td>
<td>{$lng['admin']['templates']['NAME']}</td>
</tr>
<tr>
<td><em>{COMPANY}</em></td>
<td>{$lng['admin']['templates']['COMPANY']}</td>
</tr>
<tr> <tr>
<td><em>{USERNAME}</em></td> <td><em>{USERNAME}</em></td>
<td>{$lng['admin']['templates']['USERNAME']}</td> <td>{$lng['admin']['templates']['USERNAME']}</td>
@@ -90,10 +94,6 @@ $header
</if> </if>
</if> </if>
<if ($template_name == 'password_reset')> <if ($template_name == 'password_reset')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{USERNAME}</em></td> <td><em>{USERNAME}</em></td>
<td>{$lng['admin']['templates']['USERNAME']}</td> <td>{$lng['admin']['templates']['USERNAME']}</td>
@@ -140,10 +140,6 @@ $header
</tr> </tr>
</if> </if>
<if ($template_name == 'new_database_by_customer')> <if ($template_name == 'new_database_by_customer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{DB_NAME}</em></td> <td><em>{DB_NAME}</em></td>
<td>{$lng['admin']['templates']['DB_NAME']}</td> <td>{$lng['admin']['templates']['DB_NAME']}</td>
@@ -166,10 +162,6 @@ $header
</tr> </tr>
</if> </if>
<if ($template_name == 'new_ftpaccount_by_customer')> <if ($template_name == 'new_ftpaccount_by_customer')>
<tr>
<td><em>{SALUTATION}</em></td>
<td>{$lng['admin']['templates']['SALUTATION']}</td>
</tr>
<tr> <tr>
<td><em>{USR_NAME}</em></td> <td><em>{USR_NAME}</em></td>
<td>{$lng['admin']['templates']['USR_NAME']}</td> <td>{$lng['admin']['templates']['USR_NAME']}</td>
@@ -190,4 +182,3 @@ $header
</article> </article>
$footer $footer

View File

@@ -0,0 +1,46 @@
$(document).ready(function() {
var getUrlParameter = function getUrlParameter(sParam) {
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : sParameterName[1];
}
}
};
/**
* check for internal ip and output a notice if private-range ip is given
*/
$('#ip').change(function() {
var ipval = $(this).val();
if (ipval.length > 0) {
var sid = getUrlParameter('s');
$.ajax({
url: "admin_ipsandports.php?s="+sid+"&page=overview&action=jqCheckIP",
type: "POST",
data: {
ip: ipval
},
dataType: "json",
success: function(json) {
if (json != 0) {
$('#ip').parent().append(json);
} else {
$('#ipnote').remove();
}
},
error: function(a, b) {
console.log(a, b);
}
});
}
});
});

View File

@@ -27,7 +27,7 @@
<table class="full hl"> <table class="full hl">
<thead> <thead>
<tr> <tr>
<th>{$lng['domains']['domainname']}&nbsp;{$arrowcode['d.domain']}</th> <th>{$lng['domains']['domainname']}&nbsp;{$arrowcode['d.domain_ace']}</th>
<th>{$lng['panel']['path']}</th> <th>{$lng['panel']['path']}</th>
<th>{$lng['panel']['options']}</th> <th>{$lng['panel']['options']}</th>
</tr> </tr>

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Default-Style" content="text/css" />
<link rel="stylesheet" href="templates/Sparkle/assets/css/main.css" />
<!--[if IE]><link rel="stylesheet" href="templates/Sparkle/assets/css/main_ie.css" /><![endif]-->
<!--[if lt IE 9]><script src="js/html5shiv.min.js"></script><![endif]-->
<link href="templates/Sparkle/assets/img/favicon.ico" rel="icon" type="image/x-icon" />
<title>Froxlor Server Management Panel - Requirements not met</title>
</head>
<body>
<div class="loginpage">
<article class="errorbox bradius">
<header class="dark">
<img src="templates/Sparkle/assets/img/logo.png" alt="Froxlor Server Management Panel" />
</header>
<section class="errorsec">
<div class="errorcontainer bradius">
<div class="errortitle">Whoops!</div>
<div class="error">
<p>It seems you are using an older version of PHP</p>
<p>&nbsp;</p>
<p>Froxlor requires at least PHP version <FROXLOR_PHPMIN><br />The installed version is: <CURRENT_VERSION></p>
</div>
</div>
<aside class="right">
<a href="index.php" title="Click to refresh">Refresh</a>
</aside>
</section>
</article>
</div>
<footer>
<span>
Froxlor &copy; 2009-<CURRENT_YEAR> by <a href="https://www.froxlor.org/" rel="external">the Froxlor Team</a>
</span>
</footer>
</body>
</html>

View File

@@ -64,7 +64,7 @@ class CertificatesTest extends TestCase
'ssl_key_file' => $certdata['key'] 'ssl_key_file' => $certdata['key']
))->add(); ))->add();
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
$this->assertEquals(5, $result['domainid']); $this->assertEquals(6, $result['domainid']);
} }
public function testAdminCertificatesList() public function testAdminCertificatesList()
@@ -148,7 +148,7 @@ class CertificatesTest extends TestCase
'ssl_key_file' => $certdata['key'] 'ssl_key_file' => $certdata['key']
))->update(); ))->update();
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
$this->assertEquals(5, $result['domainid']); $this->assertEquals(6, $result['domainid']);
$this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file'])); $this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file']));
} }

View File

@@ -347,4 +347,27 @@ class DomainsTest extends TestCase
$this->expectExceptionMessage("Not allowed to execute given command."); $this->expectExceptionMessage("Not allowed to execute given command.");
$json_result = Domains::getLocal($customer_userdata)->listingCount(); $json_result = Domains::getLocal($customer_userdata)->listingCount();
} }
public function testAdminIdnDomainsAdd()
{
global $admin_userdata;
// get customer
$json_result = Customers::getLocal($admin_userdata, array(
'loginname' => 'test1'
))->get();
$customer_userdata = json_decode($json_result, true)['data'];
$data = [
'domain' => 'täst.local',
'customerid' => $customer_userdata['customerid']
];
$json_result = Domains::getLocal($admin_userdata, $data)->add();
$result = json_decode($json_result, true)['data'];
$this->assertEquals($customer_userdata['documentroot'] . 'xn--tst-qla.local/', $result['documentroot']);
$this->assertEquals('xn--tst-qla.local', $result['domain']);
$this->assertEquals('täst.local', $result['domain_ace']);
Domains::getLocal($admin_userdata, [
'domainname' => 'täst.local'
])->delete();
}
} }

View File

@@ -25,6 +25,9 @@ class MailsTest extends TestCase
{ {
global $admin_userdata; global $admin_userdata;
// set domains as hidden to test whether the internal flag works
Settings::Set('panel.customer_hide_options', 'domains', true);
// get customer // get customer
$json_result = Customers::getLocal($admin_userdata, array( $json_result = Customers::getLocal($admin_userdata, array(
'loginname' => 'test1' 'loginname' => 'test1'
@@ -39,6 +42,9 @@ class MailsTest extends TestCase
$result = json_decode($json_result, true)['data']; $result = json_decode($json_result, true)['data'];
$this->assertEquals("info@test2.local", $result['email_full']); $this->assertEquals("info@test2.local", $result['email_full']);
$this->assertEquals(0, $result['iscatchall']); $this->assertEquals(0, $result['iscatchall']);
// reset setting
Settings::Set('panel.customer_hide_options', '', true);
} }
public function testAdminEmailsAdd() public function testAdminEmailsAdd()